From a5a9d416137bb2a39a22d2c3c3979741fd027d01 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 8 Dec 2021 10:33:16 +0200 Subject: [PATCH 001/120] Update access nodes based on the Governance SC (partial). --- packages/chain/chain.go | 2 + packages/chain/chainimpl/chainimpl.go | 58 ++++++++++++++++-- packages/chain/committee/committee.go | 9 +++ packages/peering/domain/domain.go | 68 +++++++++++++++------- packages/peering/peering.go | 1 + packages/vm/core/governance/accessnodes.go | 20 +++++++ 6 files changed, 130 insertions(+), 28 deletions(-) diff --git a/packages/chain/chain.go b/packages/chain/chain.go index 133d5f9460..f3be14593d 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -8,6 +8,7 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/chain/messages" @@ -83,6 +84,7 @@ type Committee interface { RunACSConsensus(value []byte, sessionID uint64, stateIndex uint32, callback func(sessionID uint64, acs [][]byte)) GetOtherValidatorsPeerIDs() []string GetRandomValidators(upToN int) []string + MemberPubKeys() []*ed25519.PublicKey } type NodeConnection interface { diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index 94b37e143b..fed4f37aa4 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -9,6 +9,7 @@ import ( "github.com/iotaledger/goshimmer/packages/ledgerstate" txstream "github.com/iotaledger/goshimmer/packages/txstream/client" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/logger" @@ -31,7 +32,9 @@ import ( "github.com/iotaledger/wasp/packages/util" "github.com/iotaledger/wasp/packages/util/pipe" "github.com/iotaledger/wasp/packages/vm/core/blocklog" + "github.com/iotaledger/wasp/packages/vm/core/governance" "github.com/iotaledger/wasp/packages/vm/processors" + "github.com/iotaledger/wasp/packages/vm/viewcontext" "go.uber.org/atomic" "golang.org/x/xerrors" ) @@ -39,11 +42,12 @@ import ( const maxMsgBuffer = 1000 var ( - _ chain.Chain = &chainObj{} - _ chain.ChainCore = &chainObj{} - _ chain.ChainEntry = &chainObj{} - _ chain.ChainRequests = &chainObj{} - _ chain.ChainEvents = &chainObj{} + _ chain.Chain = &chainObj{} + _ chain.ChainCore = &chainObj{} + _ chain.ChainEntry = &chainObj{} + _ chain.ChainRequests = &chainObj{} + _ chain.ChainEvents = &chainObj{} + _ map[ed25519.PublicKey]bool // We rely on value comparison on the pubkeys, just assert that here. ) type chainObj struct { @@ -93,7 +97,7 @@ func NewChain( chainID *iscp.ChainID, log *logger.Logger, txstreamClient *txstream.Client, - peerNetConfig registry.PeerNetworkConfigProvider, + peerNetConfig registry.PeerNetworkConfigProvider, // TODO: Remove it. db kvstore.KVStore, netProvider peering.NetworkProvider, dksProvider registry.DKShareRegistryProvider, @@ -248,6 +252,7 @@ func (c *chainObj) processChainTransition(msg *chain.ChainTransitionEventData) { chain.LogStateTransition(msg, reqids, c.log) c.mempoolLastCleanedIndex = stateIndex + c.updateChainNodes() } else { c.log.Debugf("processChainTransition state %d: output %s is governance updated; state hash %s", stateIndex, iscp.OID(msg.ChainOutput.ID()), msg.VirtualState.StateCommitment().String()) @@ -262,6 +267,46 @@ func (c *chainObj) processChainTransition(msg *chain.ChainTransitionEventData) { c.log.Debugf("processChainTransition completed: state index: %d, state hash: %s", stateIndex, msg.VirtualState.StateCommitment().String()) } +func (c *chainObj) updateChainNodes() { + stateIndex := c.consensus.GetStatusSnapshot().StateIndex + govAccessNodes := make([]ed25519.PublicKey, 0) + if stateIndex > 0 { + res, err := viewcontext.NewFromChain(c).CallView( + governance.Contract.Hname(), + governance.FuncGetChainNodes.Hname(), + governance.GetChainNodesRequest{}.AsDict(), + ) + if err != nil { + c.log.Panicf("unable to read the governance contract state: %v", err) + } + govAccessNodes = governance.NewGetChainNodesResponseFromDict(res).AccessNodes + } + + // + // Collect the new set of access nodes in the communication domain. + // They include the committee nodes as well as the explicitly set access nodes. + newMembers := make(map[ed25519.PublicKey]bool) + newMembers[*c.netProvider.Self().PubKey()] = true + cmt := c.getCommittee() + if cmt != nil { + for _, cm := range cmt.MemberPubKeys() { + newMembers[*cm] = true + } + } + for _, newAccessNode := range govAccessNodes { + newMembers[newAccessNode] = true + } + + // + // Pass it to the underlying domain to make a graceful update. + newMemberList := make([]*ed25519.PublicKey, 0) + for pubKey := range newMembers { + pubKeyCopy := pubKey + newMemberList = append(newMemberList, &pubKeyCopy) + } + c.chainPeers.UpdatePeers(newMemberList) +} + func (c *chainObj) publishNewBlockEvents(blockIndex uint32) { if blockIndex == 0 { // don't run on state #0, root contracts not initialized yet. @@ -386,4 +431,5 @@ func (c *chainObj) setCommittee(cmt chain.Committee) { cmt: cmt, }) } + c.updateChainNodes() } diff --git a/packages/chain/committee/committee.go b/packages/chain/committee/committee.go index 027422dbda..801cd59625 100644 --- a/packages/chain/committee/committee.go +++ b/packages/chain/committee/committee.go @@ -8,6 +8,7 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/chain" "github.com/iotaledger/wasp/packages/chain/consensus/commonsubset" @@ -175,6 +176,14 @@ func (c *committee) PeerStatus() []*chain.PeerStatus { return ret } +func (c *committee) MemberPubKeys() []*ed25519.PublicKey { + ret := make([]*ed25519.PublicKey, 0) + for _, peer := range c.validatorNodes.AllNodes() { + ret = append(ret, peer.PubKey()) + } + return ret +} + func (c *committee) Close() { c.acsRunner.Close() c.isReady.Store(false) diff --git a/packages/peering/domain/domain.go b/packages/peering/domain/domain.go index 14bbc71cae..bad2defb75 100644 --- a/packages/peering/domain/domain.go +++ b/packages/peering/domain/domain.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package domain import ( @@ -5,6 +8,7 @@ import ( "sort" "sync" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/util" @@ -92,31 +96,51 @@ func (d *DomainImpl) GetRandomPeers(upToNumPeers int) []string { return ret } -func (d *DomainImpl) AddPeer(netID string) error { - d.mutex.Lock() - defer d.mutex.Unlock() - - if _, ok := d.nodes[netID]; ok { - return nil +func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { + d.mutex.RLock() + defer d.mutex.RUnlock() + changed := false + // + // Add new peers. + for _, newPeerPubKey := range newPeerPubKeys { + found := false + for _, existingPeer := range d.nodes { + if *existingPeer.PubKey() == *newPeerPubKey { + found = true + break + } + } + if !found { + newPeerSender, err := d.netProvider.PeerByPubKey(newPeerPubKey) + if err != nil { + // TODO: Maybe more control should be needed here. Distinguish the mandatory and the optional nodes? + d.log.Warnf("Peer with pubKey=%v not found, will be ignored for now, reason: %v", newPeerPubKey.String(), err) + } else { + changed = true + d.nodes[newPeerSender.NetID()] = newPeerSender + d.log.Infof("Domain peer added, pubKey=%v, netID=%v", newPeerSender.PubKey().String(), newPeerSender.NetID()) + } + } } - if netID == d.netProvider.Self().NetID() { - return nil + // + // Remove peers that are not needed anymore. + for _, oldPeer := range d.nodes { + found := false + for _, newPeerPubKey := range newPeerPubKeys { + if *oldPeer.PubKey() == *newPeerPubKey { + found = true + break + } + } + if !found && (*oldPeer.PubKey() != *d.netProvider.Self().PubKey()) { + changed = true + delete(d.nodes, oldPeer.NetID()) + d.log.Infof("Domain peer removed, pubKey=%v, netID=%v", oldPeer.PubKey().String(), oldPeer.NetID()) + } } - peer, err := d.netProvider.PeerByNetID(netID) - if err != nil { - return err + if changed { + d.reshufflePeers() } - d.nodes[netID] = peer - d.reshufflePeers() - - return nil -} - -func (d *DomainImpl) RemovePeer(netID string) { - d.mutex.Lock() - defer d.mutex.Unlock() - delete(d.nodes, netID) - d.reshufflePeers() } func (d *DomainImpl) ReshufflePeers(seedBytes ...[]byte) { diff --git a/packages/peering/peering.go b/packages/peering/peering.go index 0b4005136f..12c21a9257 100644 --- a/packages/peering/peering.go +++ b/packages/peering/peering.go @@ -92,6 +92,7 @@ type GroupProvider interface { type PeerDomainProvider interface { ReshufflePeers(seedBytes ...[]byte) GetRandomPeers(upToNumPeers int) []string + UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) Attach(receiver byte, callback func(recv *PeerMessageIn)) interface{} Detach(attachID interface{}) SendMsgByNetID(netID string, msgReceiver byte, msgType byte, msgData []byte) diff --git a/packages/vm/core/governance/accessnodes.go b/packages/vm/core/governance/accessnodes.go index 39afeea870..15b81b770c 100644 --- a/packages/vm/core/governance/accessnodes.go +++ b/packages/vm/core/governance/accessnodes.go @@ -45,6 +45,26 @@ func NewAccessNodeInfoFromBytes(pubKey, value []byte) (*AccessNodeInfo, error) { return &a, nil } +func NewAccessNodeInfoListFromMap(infoMap *collections.ImmutableMap) ([]*AccessNodeInfo, error) { + res := make([]*AccessNodeInfo, 0) + var accErr error + err := infoMap.Iterate(func(elemKey, value []byte) bool { + var a *AccessNodeInfo + if a, accErr = NewAccessNodeInfoFromBytes(elemKey, value); accErr != nil { + return false + } + res = append(res, a) + return true + }) + if accErr != nil { + return nil, xerrors.Errorf("failed to iterate over AccessNodeInfo list: %v", accErr) + } + if err != nil { + return nil, xerrors.Errorf("failed to iterate over AccessNodeInfo list: %v", err) + } + return res, nil +} + func (a *AccessNodeInfo) Bytes() []byte { w := bytes.Buffer{} // NodePubKey stored as a map key. From 2bc5980b9e1bbb3ec8bb6b298893c3ff38511a61 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 8 Dec 2021 10:33:38 +0200 Subject: [PATCH 002/120] UML --- .../uml/class__Governance__Governance.png | Bin 0 -> 30280 bytes .../uml/sd__Governance__Governance.png | Bin 0 -> 47788 bytes ...-OwnershipFSM__Governance-OwnershipFSM.png | Bin 0 -> 19647 bytes ...-RotationFSM__GovernanceSC-RotationFSM.png | Bin 0 -> 30893 bytes .../uml/uc__Use_Cases__Governance.png | Bin 0 -> 69857 bytes documentation/uml/wasp.mdzip | Bin 103242 -> 134738 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 documentation/uml/class__Governance__Governance.png create mode 100644 documentation/uml/sd__Governance__Governance.png create mode 100644 documentation/uml/stm__Governance-OwnershipFSM__Governance-OwnershipFSM.png create mode 100644 documentation/uml/stm__GovernanceSC-RotationFSM__GovernanceSC-RotationFSM.png create mode 100644 documentation/uml/uc__Use_Cases__Governance.png diff --git a/documentation/uml/class__Governance__Governance.png b/documentation/uml/class__Governance__Governance.png new file mode 100644 index 0000000000000000000000000000000000000000..009662542902b2842b590af06d21c5e83ca30884 GIT binary patch literal 30280 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 HuDAaey=5+X literal 0 HcmV?d00001 diff --git a/documentation/uml/sd__Governance__Governance.png b/documentation/uml/sd__Governance__Governance.png new file mode 100644 index 0000000000000000000000000000000000000000..1ddd3b9c7f5f38fbccbdb802e9c5b111ecbab49f GIT binary patch literal 47788 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 Tmk=q)ht6Vk)N@W={NsNBOI@2G literal 0 HcmV?d00001 diff --git a/documentation/uml/stm__Governance-OwnershipFSM__Governance-OwnershipFSM.png b/documentation/uml/stm__Governance-OwnershipFSM__Governance-OwnershipFSM.png new file mode 100644 index 0000000000000000000000000000000000000000..e8ecb916e037d2c89709217457e8d249f7e83238 GIT binary patch literal 19647 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
{{ $c.Error }}
{{ end }} - {{if not $c.Error}}{{ len $c.ChainRecord.Peers }}{{ end }} + {{if not $c.Error}}{{ $c.CommitteeSize }}{{ end }} {{if not $c.Error}}{{ len $c.RootInfo.Contracts }}{{ end }} - {{ if $c.ChainRecord.Active }} yes {{ else }} no {{ end }} + {{ if $c.Active }} yes {{ else }} no {{ end }} {{end}} diff --git a/packages/peering/config.go b/packages/peering/config.go index 6c06489d8a..b0ce78565f 100644 --- a/packages/peering/config.go +++ b/packages/peering/config.go @@ -8,56 +8,9 @@ import ( "net" "strconv" - "github.com/iotaledger/wasp/packages/util" "golang.org/x/xerrors" ) -// StaticPeerNetworkConfig in an implementation of registry.PeerNetworkConfigProvider. It does not change -// Alternatively, the configuration of peers behind could change and be dependent on chain -type StaticPeerNetworkConfig struct { - ownNetID string - peeringPort int - neighbors []string -} - -// NewStaticPeerNetworkConfigProvider is a configuration of the peer environment which does not change -func NewStaticPeerNetworkConfigProvider(ownNetID string, peeringPort int, neighbors ...string) (*StaticPeerNetworkConfig, error) { - if err := CheckMyNetID(ownNetID, peeringPort); err != nil { - return nil, xerrors.Errorf("NewStaticPeerNetworkConfigProvider: %w", err) - } - if !util.AllDifferentStrings(neighbors) { - return nil, xerrors.Errorf("NewStaticPeerNetworkConfigProvider: neighbors must all be different") - } - neigh := make([]string, 0, len(neighbors)) - // eliminate own id from the list - for _, n := range neighbors { - if n != ownNetID { - neigh = append(neigh, n) - } - } - return &StaticPeerNetworkConfig{ - ownNetID: ownNetID, - peeringPort: peeringPort, - neighbors: neigh, - }, nil -} - -func (p *StaticPeerNetworkConfig) OwnNetID() string { - return p.ownNetID -} - -func (p *StaticPeerNetworkConfig) PeeringPort() int { - return p.peeringPort -} - -func (p *StaticPeerNetworkConfig) Neighbors() []string { - return p.neighbors -} - -func (p *StaticPeerNetworkConfig) String() string { - return fmt.Sprintf("PeerConfig( ownNetID: %s, peeringPort: %d, neighbors: %+v )", p.OwnNetID(), p.PeeringPort(), p.Neighbors()) -} - // Check, if NetID is of proper format. func CheckNetID(netID string) error { sHost, sPort, err := net.SplitHostPort(netID) diff --git a/packages/peering/config_test.go b/packages/peering/config_test.go deleted file mode 100644 index 5529fb2181..0000000000 --- a/packages/peering/config_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package peering - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestConfigBasic(t *testing.T) { - peers := []string{"localhost:4000", "localhost:4001", "localhost:4002", "localhost:4003"} - t.Run("happy path", func(t *testing.T) { - c, err := NewStaticPeerNetworkConfigProvider(peers[0], 4000, peers[1:]...) - require.NoError(t, err) - require.EqualValues(t, peers[0], c.OwnNetID()) - }) - t.Run("eliminate own netid", func(t *testing.T) { - c, err := NewStaticPeerNetworkConfigProvider(peers[1], 4001, peers...) - require.NoError(t, err) - require.EqualValues(t, len(peers)-1, len(c.Neighbors())) - }) - t.Run("wrong port", func(t *testing.T) { - _, err := NewStaticPeerNetworkConfigProvider(peers[0], 4001, peers...) - require.Error(t, err) - }) - t.Run("duplicates", func(t *testing.T) { - _, err := NewStaticPeerNetworkConfigProvider(peers[0], 4000, peers[1], peers[2], peers[1]) - require.Error(t, err) - }) -} diff --git a/packages/registry/chainrecord.go b/packages/registry/chainrecord.go index f05051ff85..f56be1403d 100644 --- a/packages/registry/chainrecord.go +++ b/packages/registry/chainrecord.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package registry import ( @@ -12,7 +15,6 @@ import ( // TODO optimize, no need for a persistent structure, simple activity tag is enough type ChainRecord struct { ChainID *iscp.ChainID - Peers []string Active bool } @@ -28,22 +30,6 @@ func FromMarshalUtil(mu *marshalutil.MarshalUtil) (*ChainRecord, error) { if err != nil { return nil, err } - numPeers, err := mu.ReadUint16() - if err != nil { - return nil, err - } - ret.Peers = make([]string, numPeers) - for i := uint16(0); i < numPeers; i++ { - strSize, err := mu.ReadUint16() - if err != nil { - return nil, err - } - d, err := mu.ReadBytes(int(strSize)) - if err != nil { - return nil, err - } - ret.Peers[i] = string(d) - } return ret, nil } @@ -54,19 +40,12 @@ func ChainRecordFromBytes(data []byte) (*ChainRecord, error) { func (rec *ChainRecord) Bytes() []byte { mu := marshalutil.New().WriteBytes(rec.ChainID.Bytes()). - WriteBool(rec.Active). - WriteUint16(uint16(len(rec.Peers))) - for _, s := range rec.Peers { - b := []byte(s) - mu.WriteUint16(uint16(len(b))). - WriteBytes(b) - } + WriteBool(rec.Active) return mu.Bytes() } func (rec *ChainRecord) String() string { ret := "ChainID: " + rec.ChainID.String() + "\n" - ret += fmt.Sprintf(" Peers: %+v\n", rec.Peers) ret += fmt.Sprintf(" Active: %v\n", rec.Active) return ret } diff --git a/packages/registry/chainrecord_test.go b/packages/registry/chainrecord_test.go index 836a2452e3..20ee7ca132 100644 --- a/packages/registry/chainrecord_test.go +++ b/packages/registry/chainrecord_test.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package registry import ( @@ -12,7 +15,6 @@ func TestChainRecord(t *testing.T) { rec := ChainRecord{ ChainID: chainID, - Peers: []string{"a", "b", "c"}, Active: false, } recBack, err := ChainRecordFromBytes(rec.Bytes()) @@ -25,7 +27,6 @@ func TestChainRecord(t *testing.T) { rec = ChainRecord{ ChainID: chainID, - Peers: []string{"k", "l"}, Active: true, } require.True(t, rec.Active) diff --git a/packages/registry/providers.go b/packages/registry/providers.go index 3278d90983..8d05afdaa2 100644 --- a/packages/registry/providers.go +++ b/packages/registry/providers.go @@ -28,14 +28,6 @@ type NodeIdentityProvider interface { GetNodePublicKey() (*ed25519.PublicKey, error) } -// PeerNetworkConfigProvider access to node and chain configuration: a list of netIDs of potential peers -type PeerNetworkConfigProvider interface { // TODO: KP: Remove or redesign. - OwnNetID() string - PeeringPort() int - Neighbors() []string - String() string -} - // DKShareRegistryProvider stands for a partial registry interface, needed for this package. // It should be implemented by registry.impl type DKShareRegistryProvider interface { diff --git a/packages/webapi/model/chain_record.go b/packages/webapi/model/chain_record.go index 8b5860bda9..e23440db39 100644 --- a/packages/webapi/model/chain_record.go +++ b/packages/webapi/model/chain_record.go @@ -1,18 +1,19 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package model import "github.com/iotaledger/wasp/packages/registry" type ChainRecord struct { - ChainID ChainID `swagger:"desc(ChainID (base58-encoded))"` - Active bool `swagger:"desc(Whether or not the chain is active)"` - Peers []string `swagger:"desc(List of peers/access nodes (network IDs))"` + ChainID ChainID `swagger:"desc(ChainID (base58-encoded))"` + Active bool `swagger:"desc(Whether or not the chain is active)"` } func NewChainRecord(rec *registry.ChainRecord) *ChainRecord { return &ChainRecord{ ChainID: NewChainID(rec.ChainID), Active: rec.Active, - Peers: rec.Peers, } } @@ -20,6 +21,5 @@ func (bd *ChainRecord) Record() *registry.ChainRecord { return ®istry.ChainRecord{ ChainID: bd.ChainID.ChainID(), Active: bd.Active, - Peers: bd.Peers, } } From e4f52baffb48b85a7b704d95b5b93ae6b651daf1 Mon Sep 17 00:00:00 2001 From: shawkyz Date: Mon, 13 Dec 2021 22:58:04 +0100 Subject: [PATCH 007/120] parametarize exposed host --- tools/devnet/docker-compose.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/devnet/docker-compose.yml b/tools/devnet/docker-compose.yml index c2b370017e..fcea1419c7 100644 --- a/tools/devnet/docker-compose.yml +++ b/tools/devnet/docker-compose.yml @@ -17,10 +17,10 @@ services: - "7000/tcp" # Wasp Dashboard - "9090/tcp" # Wasp WebAPI ports: - - "127.0.0.1:4000:4000/tcp" # Peering - - "127.0.0.1:5550:5550/tcp" # Nano MSG - - "127.0.0.1:7000:7000/tcp" # Wasp Dashboard - - "127.0.0.1:9090:9090/tcp" # Wasp WebAPI + - "${HOST:-127.0.0.1}:4000:4000/tcp" # Peering + - "${HOST:-127.0.0.1}:5550:5550/tcp" # Nano MSG + - "${HOST:-127.0.0.1}:7000:7000/tcp" # Wasp Dashboard + - "${HOST:-127.0.0.1}:9090:9090/tcp" # Wasp WebAPI devnet_goshimmer: restart: always container_name: devnet_goshimmer @@ -42,11 +42,11 @@ services: - ./goshimmer.config.json:/tmp/config.json:ro - ./snapshot.bin:/tmp/snapshot.bin:ro ports: - - "127.0.0.1:5000:5000/tcp" # TX Stream - - "127.0.0.1:8080:8080/tcp" # GoShimmer API - - "127.0.0.1:8081:8081/tcp" # GoShimmer Dashboard - - "127.0.0.1:9000:9000/tcp" # Analysis Dashboard - - "127.0.0.1:9312:9312/tcp" # Prometheus + - "${HOST:-127.0.0.1}:5000:5000/tcp" # TX Stream + - "${HOST:-127.0.0.1}:8080:8080/tcp" # GoShimmer API + - "${HOST:-127.0.0.1}:8081:8081/tcp" # GoShimmer Dashboard + - "${HOST:-127.0.0.1}:9000:9000/tcp" # Analysis Dashboard + - "${HOST:-127.0.0.1}:9312:9312/tcp" # Prometheus expose: - "1888/tcp" # Analysis Server (within Docker network) - "5000/tcp" # TXStream From c39cbb59c76c8970db0b7ff1b29b93c5ee860172 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Tue, 14 Dec 2021 03:11:52 +0100 Subject: [PATCH 008/120] change index to have explicit type of number --- tools/schema/generator/clienttemplates/events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/schema/generator/clienttemplates/events.go b/tools/schema/generator/clienttemplates/events.go index ca14b503d1..540c087f74 100644 --- a/tools/schema/generator/clienttemplates/events.go +++ b/tools/schema/generator/clienttemplates/events.go @@ -43,7 +43,7 @@ $#each event eventInterfaceField `, // ******************************* "eventHandler": ` - '$package.$evtName': (index) => { + '$package.$evtName': (index : number) => { const evt: Event$EvtName = { timestamp: Number(message[++index]), $#each event eventHandlerField From 01a974b40d7cc5722fec71c4a951d29b350c2693 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Tue, 14 Dec 2021 09:09:56 -0800 Subject: [PATCH 009/120] Initial client code generation --- contracts/wasm/erc721/schema.yaml | 10 +- .../wasm/fairauction/test/fairauction_bg.wasm | Bin 42692 -> 42692 bytes .../src/lib/wasp_client/basic_client.ts | 6 +- .../fairroulette/test/fairroulette_bg.wasm | Bin 40086 -> 40121 bytes contracts/wasm/testcore/test/testcore_bg.wasm | Bin 53153 -> 53153 bytes .../wasm/testwasmlib/test/testwasmlib_bg.wasm | Bin 41295 -> 41341 bytes .../tokenregistry/test/tokenregistry_bg.wasm | Bin 32026 -> 32014 bytes .../sbtests/sbtestsc/testcore_bg.wasm | Bin 53153 -> 53153 bytes .../vm/wasmlib/go/wasmlib/client/arguments.go | 147 ++ .../vm/wasmlib/go/wasmlib/client/event.go | 107 + .../vm/wasmlib/go/wasmlib/client/results.go | 120 + .../vm/wasmlib/go/wasmlib/client/service.go | 35 + .../vm/wasmlib/go/wasmlib/client/types.go | 11 + packages/vm/wasmlib/go/wasmlib/context.go | 3 + packages/vm/wasmlib/src/context.rs | 3 + .../vm/wasmlib/ts/wasmlib/client/arguments.ts | 146 ++ .../ts/wasmlib/client/buffer/index.d.ts | 194 ++ .../wasmlib/ts/wasmlib/client/buffer/index.js | 2115 +++++++++++++++++ .../ts/wasmlib/client/buffer/readme.md | 3 + .../wasmlib/ts/wasmlib/client/config.dev.js | 10 + .../ts/wasmlib/client/crypto/base58.ts | 148 ++ .../ts/wasmlib/client/crypto/ed25519.ts | 36 + .../wasmlib/ts/wasmlib/client/crypto/hname.ts | 25 + .../wasmlib/ts/wasmlib/client/crypto/index.ts | 6 + .../wasmlib/ts/wasmlib/client/crypto/seed.ts | 97 + .../vm/wasmlib/ts/wasmlib/client/event.ts | 93 + .../vm/wasmlib/ts/wasmlib/client/index.ts | 8 + .../vm/wasmlib/ts/wasmlib/client/results.ts | 105 + .../vm/wasmlib/ts/wasmlib/client/service.ts | 90 + .../wasmlib/ts/wasmlib/client/tsconfig.json | 11 + .../vm/wasmlib/ts/wasmlib/client/types.ts | 28 + packages/vm/wasmlib/ts/wasmlib/context.ts | 3 + packages/vm/wasmlib/ts/wasmlib/host.ts | 2 +- .../{generator_client.go => clientbase.go} | 33 +- .../generator/clienttemplates/alltemplates.go | 87 - tools/schema/generator/clienttemplates/app.go | 22 - .../generator/clienttemplates/events.go | 58 - .../generator/clienttemplates/service.go | 82 - tools/schema/generator/emitter.go | 2 + tools/schema/generator/generator.go | 20 +- tools/schema/generator/generator_goclient.go | 19 + tools/schema/generator/generator_tsclient.go | 19 + .../goclienttemplates/alltemplates.go | 165 ++ .../generator/goclienttemplates/events.go | 44 + .../generator/goclienttemplates/funcs.go | 19 + .../generator/goclienttemplates/service.go | 132 + .../tsclienttemplates/alltemplates.go | 85 + .../generator/tsclienttemplates/events.go | 39 + .../generator/tsclienttemplates/funcs.go | 19 + .../generator/tsclienttemplates/service.go | 127 + tools/schema/main.go | 22 +- 51 files changed, 4266 insertions(+), 290 deletions(-) create mode 100644 packages/vm/wasmlib/go/wasmlib/client/arguments.go create mode 100644 packages/vm/wasmlib/go/wasmlib/client/event.go create mode 100644 packages/vm/wasmlib/go/wasmlib/client/results.go create mode 100644 packages/vm/wasmlib/go/wasmlib/client/service.go create mode 100644 packages/vm/wasmlib/go/wasmlib/client/types.go create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/arguments.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/buffer/index.d.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/buffer/index.js create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/buffer/readme.md create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/config.dev.js create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/crypto/base58.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/crypto/ed25519.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/crypto/hname.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/crypto/index.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/crypto/seed.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/event.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/index.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/results.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/service.ts create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json create mode 100644 packages/vm/wasmlib/ts/wasmlib/client/types.ts rename tools/schema/generator/{generator_client.go => clientbase.go} (55%) delete mode 100644 tools/schema/generator/clienttemplates/alltemplates.go delete mode 100644 tools/schema/generator/clienttemplates/app.go delete mode 100644 tools/schema/generator/clienttemplates/events.go delete mode 100644 tools/schema/generator/clienttemplates/service.go create mode 100644 tools/schema/generator/generator_goclient.go create mode 100644 tools/schema/generator/generator_tsclient.go create mode 100644 tools/schema/generator/goclienttemplates/alltemplates.go create mode 100644 tools/schema/generator/goclienttemplates/events.go create mode 100644 tools/schema/generator/goclienttemplates/funcs.go create mode 100644 tools/schema/generator/goclienttemplates/service.go create mode 100644 tools/schema/generator/tsclienttemplates/alltemplates.go create mode 100644 tools/schema/generator/tsclienttemplates/events.go create mode 100644 tools/schema/generator/tsclienttemplates/funcs.go create mode 100644 tools/schema/generator/tsclienttemplates/service.go diff --git a/contracts/wasm/erc721/schema.yaml b/contracts/wasm/erc721/schema.yaml index 450632f03a..bbb5f0076e 100644 --- a/contracts/wasm/erc721/schema.yaml +++ b/contracts/wasm/erc721/schema.yaml @@ -65,18 +65,18 @@ views: params: owner: AgentID // account owner results: - amount: Uint64 //amount of tokens (owner) + amount: Uint64? // amount of tokens owned by owner getApproved: params: tokenID: Hash results: - approved: AgentID + approved: AgentID? isApprovedForAll: params: owner: AgentID operator: AgentID results: - approval: Bool + approval: Bool? name: results: name: String @@ -84,7 +84,7 @@ views: params: tokenID: Hash results: - owner: AgentID + owner: AgentID? symbol: results: symbol: String @@ -92,4 +92,4 @@ views: params: tokenID: Hash results: - tokenURI: String + tokenURI: String? diff --git a/contracts/wasm/fairauction/test/fairauction_bg.wasm b/contracts/wasm/fairauction/test/fairauction_bg.wasm index 86bf39de8770c3bc2aed99c8ae8fa241c58ce144..3f26ba1243a0df889582a7e01dfef512221ecce2 100644 GIT binary patch delta 175 zcmX?dmg&e@rVVlujI$@pOQAbYc6;bT@H$Ex0Ta!|QBP@t>amK`W? zqRv5hHY2wJqa#O_fUx7WjspyiQUY@(OV`VRm3sn}w$$(E7D#sFS71AbYc6;bT@H$Ex0Ta!|QBP@t>amK`W? zqRv5h4kNb$qa#O_fUx7WjspyiQUbFlOV`VRm3sn}w$$(E7D#jCS71 { const url = `chain/${chainId}/contract/${contractHName}/callview/${entryPoint}`; - const result = await this.sendRequestExt(this.configuration.WaspAPIUrl, 'get', url); + const result = await this.sendRequestExt( + this.configuration.WaspAPIUrl, + 'get', + url + ); return result.body; } diff --git a/contracts/wasm/fairroulette/test/fairroulette_bg.wasm b/contracts/wasm/fairroulette/test/fairroulette_bg.wasm index ce7b3e910f9af19e6f78ea9220b8d1c6b5a8e352..6bb26bbfeddd401ce513a605a88c8769612a3492 100644 GIT binary patch delta 4924 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 delta 4921 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 J=ihX^^uJrB^O67n delta 5412 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 diff --git a/contracts/wasm/tokenregistry/test/tokenregistry_bg.wasm b/contracts/wasm/tokenregistry/test/tokenregistry_bg.wasm index 27eecf21ae665db991bfc2bf1888784b538f89af..39244943da12f642aae2ce320deffcfb1f6427b5 100644 GIT binary patch delta 1459 zcmaJ>U1%It6u#%)+1*KJXZNmXy4%*xogqyV*=A#)yN$MSCy+plNmY_UYSom~#O{Jf zyOO3&H$P3XP1CHqxm^Vxtcuu&N~sJ=Uc`W)XnatyZz2kUpoOSUnkUblq(zFBfipAb zp8K8e`|g=}V*^SXQ2v%xK~`?&<>N5?*c|lF!5mcKp(^aI!oX|z_;NUN8J^!!-+TJG zWsqfTSVj;5g7Bg|#SZN(f#t)O7|jUga7X94Lz=44_@w-S&DE5_W+n}SA^%84<*e4% za2F`>A_y&KF#&wUCR#={K=8k`L=3^P7mwYB?lygbdeH5@50L5azFIgZ1O9=)q+pmY z$>aVvG0y^n7@rNaVSGI>h03COzgY&01^tGHP?F=i?VA*om)*DZW!9{CD)yG&x!=3f zO*Iw>UFaN_iAoESvkCS&7Tl+>gK;RykAk+o0H*TfqWm$~7QVZ<(M;Sa&YKzZ%qA&H z&?Ru-WQuB=q7)hVwfTes!$kzE57jGT{!K!HDH`Ct5M)J#KbN(AN;g;mp&lmXSmx?=0h=lkKsSY7@EpXMwU}o_0UbfV zSk|KGI^(a(pK6z3T;{i5L;WIp1moH0;7)V_{>ufz7vy@h4T^44Y)!*rgVxJfJYA3d zaf|AYBEZsA|9@Y&R*?Dlp1`=NLgU8mEO%ONMY-%(bS>H!|6B_zlIc|`Jd2Y2JRSF! zm5l}WxAc43o&`|95KH|vaH@TGrXB}M&0Z0w-5a)*1H0gid-FgDP{z*(`|)_%+E*AX zx_>$=ntu_9!Kd9DT}LP^Nu#?Xv;=}WEm;9gjPoV;neKI^rl;o-R5RaWFqn|bef==u z{?gZJZta(?M+a|1RgQkj;IJGRN^QAxWd>c^&WV&m6WEp?Yo}U8>3Dj$K+&~tUg*LY M9l7MXBcrwd0N+eltpET3 delta 1446 zcmaJ>T}&KR7@cos_NVO5?nMz6kmb&lA{xpf(WO|eJEV{zmJ$PrR76ZG+TBDcVoE7x zfm*s!WZAv#Hfqy8m{=1up_!Myn2_++HZiF`X`&CBG{zrdqYpk16Fql;q-tU|`F3XR zch9-!d}n51o!(!k#ami|&gH%u75nJ$)3ekwOS4pf_8%V* zbdKJpiCydzy;-316y&X^soH**ioU*yrHuH6qLdSBRW+hP{Q&pZ)w$^8YqmOSFE6bq z$vRcBlr}>PXbG-x;tI=%5$(W(Su#DiDOVF*vzhH@sRO#~(AgA;Z?)6ga%5?S8l-^u zH69T&dROhgP>E%T>nT&?#D*+IPe=x!|Do4c03Bqkd<(n7@Njhld$;FmBuAze^aa#} zO1BV$gdD%Kq~9q4xP&P=#-brbUUr$8Zt60T(nY-tt>s zY*Sgzx#-Vp=!^xUVJJ|fFs`I51$h0z$Fb6FL@6t-1}*;_8Pbol;+tU0f10-&_4hPa ziFso?wk;bEY!Zp<#!h8s|S57$9xC95o%Cd+4+NL-7h>?28wfc*@sE`|dkZg9q= z*S3PEUDbQ}EFRt9yNfbpkJ%G0C^KW;bOkVTPMhfu z)Pr69Kb44O(xgq5$Lpoa;Lgi9%O^X3rk})xh76q%JQnk>omx@sMt013G1g9eEWpXW ztt3&uQH-i8SKrdb_z33iiuK7?eznd(jU? z7+}iMn7q`OfdjLg97@{xvH2r??;Oc5gDJlS2(t%k)>g)395V>H%pY0X-1rx5#MKOJ zo;C=i6V9zCLjUk-w_t2zDW8%wK+EFWq=Di`sCi{?cX=u3gZ{l)aKL7v# diff --git a/packages/vm/core/testcore/sbtests/sbtestsc/testcore_bg.wasm b/packages/vm/core/testcore/sbtests/sbtestsc/testcore_bg.wasm index e3b2cafe6e54f16d91f11bd435f1d8bb01ec677c..97a155c837bdf3b46df9d975b03c73bedb71db88 100644 GIT binary patch delta 1310 zcmZ8fUu=_A6#vfGv9-3!MYe7$qy4%~Mav&Wfwj|wyLAI1BTLYrJ{f(0v`b)#qJ{+~ z1I!l8+Fo0_wcS>A5;YR>V<8%12r3&O3?V+1qTxXU4KJ)7jP*Hsm z@X=}Wd|=2$GwP)l6X*)&ad9NLjtc7UU);$nUEV- zzgUF&R;F#X@O=xXTU+hrCr%aX^!hp~*2n`ZJB4UW-L=039s8=&yFJHF}^!N80YixE< z(7QpB=a^Cr%3QYAe&7%sKb&B8(KD8uRUek0zb{JPKgT4W_%E9{K=KC18m&aNYCKz2 zO1AI5Wc?&hWI*G3=CXB7W2IbN)&0a?`6=IVhv%}~TW=0uG()+yyk)z_>RXT-<#6j( zYo)?8%w>w#eF+z)$y=j;Bz18CNAVBIBHskCazBp!rK7>8@`Co<`T#*M==Y2%mP8B< zz-%;ml%4+(X742&dY7sD#91KIe9E;-BZ-bI$WV z=e*}VH$Upgk2;QzQF(>aL7Jwi!*pQrB&8@#2g$p0{j2X?po^5CLzJOGIz#VYQtf&z zowrOqO!S>K?pQ%|&icu@mCXxfE7|NQ59%=4itXhvo_Y7!+^@RExk=xxGMF#w^hclQ!0bO;rR;deN zFH%3-gz5;>UbgXL8z&{%qRW6?(RwOaqtPdIn2T+H%t7qi7h}Mu+8NXB z+o)(wx9@dhN_$^BB0yVi65(!7$Na#}F&0b3nt@kitAS-5D;AX63Yfwte;7hnkE5&g z9R}2`t(|Ypp&Qnvop~pnRUNy=ATqaW0J^!Z9WFSJ?tceP&;BMioLr(w{Tg@A2)az+ zm;J*KtQH03Nf|KMjX18+yLrQ#RoPjd|N5?0CQ6V>1AHrSY~>_ zH1Hd!JM8K9ZN2 zvJjN%Y^AIBAe=flp=`}_ww&M_W8eA4qIBcSO!A9=tBGEc_c&H>7phdF+454ceGe7u zCixAIp8J{3RyB;2YH?Q&6FcSSoa3QRXL+1P4p+3oxi!34yGE*;Q5)@WYpb+UV;Y`f zf;W5#H>Szohki@x;wnDH-z1Bi31Z8>ANf&ghT;j)U1l}TM%r>= 8 + } + a.set(key, bytes) +} + +// Encode returns a byte array that encodes the Arguments as follows: +// Sort all keys in ascending order (very important, because this data +// will be part of the data that will be signed, so the order needs to +// be 100% deterministic). Then emit a 2-byte argument count. +// Next for each argument emit a 2-byte key length, the key prepended +// with a minus sign, a 4-byte value length, and then the value bytes. +func (a Arguments) Encode() []byte { + keys := make([]string, 0, len(a.args)) + total := 2 + for k, v := range a.args { + keys = append(keys, k) + total += 2 + 1 + len(k) + 4 + len(v) + } + sort.Strings(keys) + + buf := make([]byte, 0, total) + buf = append(buf, codec.EncodeUint16(uint16(len(keys)))...) + for _, k := range keys { + buf = append(buf, codec.EncodeUint16(uint16(len(k)+1))...) + buf = append(buf, '-') + buf = append(buf, []byte(k)...) + v := a.args[k] + buf = append(buf, codec.EncodeUint32(uint32(len(v)))...) + buf = append(buf, v...) + } + return buf +} diff --git a/packages/vm/wasmlib/go/wasmlib/client/event.go b/packages/vm/wasmlib/go/wasmlib/client/event.go new file mode 100644 index 0000000000..491e89521f --- /dev/null +++ b/packages/vm/wasmlib/go/wasmlib/client/event.go @@ -0,0 +1,107 @@ +package client + +import ( + "strconv" +) + +type Event struct { + message []string +} + +func (e *Event) Init(message []string) { + e.message = message +} + +func (e *Event) next() string { + next := e.message[0] + e.message = e.message[1:] + return next +} + +func (e *Event) NextAddress() Address { + return Address(e.next()) +} + +func (e *Event) NextAgentID() AgentID { + return AgentID(e.next()) +} + +func (e *Event) NextBytes() []byte { + return Base58Decode(e.next()) +} + +func (e *Event) NextBool() bool { + return e.next() != "0" +} + +func (e *Event) NextChainID() ChainID { + return ChainID(e.next()) +} + +func (e *Event) NextColor() Color { + return Color(e.next()) +} + +func (e *Event) NextHash() Hash { + return Hash(e.next()) +} + +func (e *Event) NextHname() Hname { + return Hname(e.nextUint(32)) +} + +func (e *Event) nextInt(bitSize int) int64 { + val, err := strconv.ParseInt(e.next(), 10, bitSize) + if err != nil { + panic("int parse error") + } + return val +} + +func (e *Event) NextInt8() int8 { + return int8(e.nextInt(8)) +} + +func (e *Event) NextInt16() int16 { + return int16(e.nextInt(16)) +} + +func (e *Event) NextInt32() int32 { + return int32(e.nextInt(32)) +} + +func (e *Event) NextInt64() int64 { + return e.nextInt(64) +} + +func (e *Event) NextRequestID() RequestID { + return RequestID(e.next()) +} + +func (e *Event) NextString() string { + return e.next() +} + +func (e *Event) nextUint(bitSize int) uint64 { + val, err := strconv.ParseUint(e.next(), 10, bitSize) + if err != nil { + panic("uint parse error") + } + return val +} + +func (e *Event) NextUint8() uint8 { + return uint8(e.nextUint(8)) +} + +func (e *Event) NextUint16() uint16 { + return uint16(e.nextUint(16)) +} + +func (e *Event) NextUint32() uint32 { + return uint32(e.nextUint(32)) +} + +func (e *Event) NextUint64() uint64 { + return e.nextUint(64) +} diff --git a/packages/vm/wasmlib/go/wasmlib/client/results.go b/packages/vm/wasmlib/go/wasmlib/client/results.go new file mode 100644 index 0000000000..1e61d6952d --- /dev/null +++ b/packages/vm/wasmlib/go/wasmlib/client/results.go @@ -0,0 +1,120 @@ +package client + +import ( + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" +) + +// The Results struct is used to gather all arguments for a smart +// contract function call and encode it into a deterministic byte array +type Results struct { + res map[string][]byte +} + +func NewResults() Results { + return Results{} +} + +func (r Results) Exists(key string) bool { + _, ok := r.res[key] + return ok +} + +func (r Results) get(key string, typeID int32) []byte { + size := wasmlib.TypeSizes[typeID] + bytes, ok := r.res[key] + if ok { + if size != 0 && len(bytes) != int(size) { + panic("invalid type size") + } + return bytes + } + // return default all-zero bytes value + return make([]byte, size) +} + +func (r Results) getBase58(key string, typeID int32) string { + return Base58Encode(r.get(key, typeID)) +} + +func (r Results) GetAddress(key string) Address { + return Address(r.getBase58(key, wasmlib.TYPE_ADDRESS)) +} + +func (r Results) GetAgentID(key string) AgentID { + return AgentID(r.getBase58(key, wasmlib.TYPE_AGENT_ID)) +} + +func (r Results) GetBytes(key string) []byte { + return r.get(key, wasmlib.TYPE_BYTES) +} + +func (r Results) GetBool(key string) bool { + return r.get(key, wasmlib.TYPE_BOOL)[0] != 0 +} + +func (r Results) GetChainID(key string) ChainID { + return ChainID(r.getBase58(key, wasmlib.TYPE_CHAIN_ID)) +} + +func (r Results) GetColor(key string) Color { + return Color(r.getBase58(key, wasmlib.TYPE_COLOR)) +} + +func (r Results) GetHash(key string) Hash { + return Hash(r.getBase58(key, wasmlib.TYPE_HASH)) +} + +func (r Results) GetHname(key string) Hname { + return Hname(r.getUint64(key, wasmlib.TYPE_HNAME)) +} + +func (r Results) GetInt8(key string) int8 { + return int8(r.get(key, wasmlib.TYPE_INT8)[0]) +} + +func (r Results) GetInt16(key string) int16 { + return int16(r.getUint64(key, wasmlib.TYPE_INT16)) +} + +func (r Results) GetInt32(key string) int32 { + return int32(r.getUint64(key, wasmlib.TYPE_INT32)) +} + +func (r Results) GetInt64(key string) int64 { + return int64(r.getUint64(key, wasmlib.TYPE_INT64)) +} + +func (r Results) GetRequestID(key string) RequestID { + return RequestID(r.getBase58(key, wasmlib.TYPE_REQUEST_ID)) +} + +func (r Results) GetString(key string) string { + return string(r.get(key, wasmlib.TYPE_STRING)) +} + +func (r Results) GetUint8(key string) uint8 { + return r.get(key, wasmlib.TYPE_INT8)[0] +} + +func (r Results) GetUint16(key string) uint16 { + return uint16(r.getUint64(key, wasmlib.TYPE_INT16)) +} + +func (r Results) GetUint32(key string) uint32 { + return uint32(r.getUint64(key, wasmlib.TYPE_INT32)) +} + +func (r Results) GetUint64(key string) uint64 { + return r.getUint64(key, wasmlib.TYPE_INT64) +} + +func (r Results) getUint64(key string, typeID int32) uint64 { + b := r.get(key, typeID) + v := uint64(0) + for i := len(b) - 1; i >= 0; i-- { + v = (v << 8) | uint64(b[i]) + } + return v +} + +// TODO Decode() from view call response into map diff --git a/packages/vm/wasmlib/go/wasmlib/client/service.go b/packages/vm/wasmlib/go/wasmlib/client/service.go new file mode 100644 index 0000000000..200d1c7d1e --- /dev/null +++ b/packages/vm/wasmlib/go/wasmlib/client/service.go @@ -0,0 +1,35 @@ +package client + +import ( + "github.com/mr-tron/base58" +) + +type ServiceClient struct{} + +type Response map[string][]byte + +type Service struct{} + +func (s *Service) Init(client ServiceClient, chainId string, scHname string, eventHandlers map[string]func([]string)) { +} + +func (s *Service) CallView(viewName string, args map[string][]byte) Response { + return nil +} + +func (s *Service) PostRequest(hFuncName string, args map[string][]byte) { +} + +///////////////////////////////////////////////////////////////// + +func Base58Decode(s string) []byte { + res, err := base58.Decode(s) + if err != nil { + panic("invalid base58 encoding") + } + return res +} + +func Base58Encode(b []byte) string { + return base58.Encode(b) +} diff --git a/packages/vm/wasmlib/go/wasmlib/client/types.go b/packages/vm/wasmlib/go/wasmlib/client/types.go new file mode 100644 index 0000000000..8731652de8 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmlib/client/types.go @@ -0,0 +1,11 @@ +package client + +type ( + Address string + AgentID string + ChainID string + Color string + Hash string + Hname uint32 + RequestID string +) diff --git a/packages/vm/wasmlib/go/wasmlib/context.go b/packages/vm/wasmlib/go/wasmlib/context.go index 01a851af57..e3fd8571ad 100644 --- a/packages/vm/wasmlib/go/wasmlib/context.go +++ b/packages/vm/wasmlib/go/wasmlib/context.go @@ -316,6 +316,9 @@ func (ctx ScFuncContext) Post(chainID ScChainID, hContract, hFunction ScHname, p // generates a random value from 0 to max (exclusive max) using a deterministic RNG func (ctx ScFuncContext) Random(max int64) int64 { + if max == 0 { + ctx.Panic("random: max parameter should be non-zero") + } state := ScMutableMap{objID: OBJ_ID_STATE} rnd := state.GetBytes(KeyRandom) seed := rnd.Value() diff --git a/packages/vm/wasmlib/src/context.rs b/packages/vm/wasmlib/src/context.rs index 9077472418..eadcb87483 100644 --- a/packages/vm/wasmlib/src/context.rs +++ b/packages/vm/wasmlib/src/context.rs @@ -345,6 +345,9 @@ impl ScFuncContext { // generates a random value from 0 to max (exclusive max) using a deterministic RNG pub fn random(&self, max: i64) -> i64 { + if max == 0 { + self.panic("random: max parameter should be non-zero"); + } let state = ScMutableMap { obj_id: OBJ_ID_STATE }; let rnd = state.get_bytes(&KEY_RANDOM); let mut seed = rnd.value(); diff --git a/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts b/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts new file mode 100644 index 0000000000..6ebff11354 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts @@ -0,0 +1,146 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmlib from "../index" +import * as client from "./index" +import {Base58} from "./crypto"; +import {Buffer} from "./buffer"; +import {SimpleBufferCursor} from "../../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; + +// The Arguments struct is used to gather all arguments for this smart +// contract function call and encode it into this deterministic byte array +export class Arguments { + args = new Map(); + + private set(key: string, val: client.Bytes): void { + this.args.set(key, val); + } + + private setBase58(key: string, val: string, typeID: client.Int32): void { + let bytes = Base58.decode(val); + if (bytes.length != wasmlib.TYPE_SIZES[typeID]) { + client.panic("invalid byte size"); + } + this.set(key, bytes); + } + + mandatory(key:string): void { + if (!this.args.has(key)) { + client.panic("missing mandatory " + key) + } + } + + setAddress(key: string, val: client.AgentID): void { + this.setBase58(key, val, wasmlib.TYPE_ADDRESS); + } + + setAgentID(key: string, val: client.AgentID): void { + this.setBase58(key, val, wasmlib.TYPE_AGENT_ID); + } + + setBool(key: string, val: boolean): void { + let bytes = Buffer.alloc(1); + if (val) { + bytes.writeUInt8(1, 0); + } + this.set(key, bytes) + } + + setBytes(key: string, val: client.Bytes): void { + this.set(key, Buffer.from(val)); + } + + setColor(key: string, val: client.Color): void { + this.setBase58(key, val, wasmlib.TYPE_COLOR); + } + + setChainID(key: string, val: client.ChainID): void { + this.setBase58(key, val, wasmlib.TYPE_CHAIN_ID); + } + + setHash(key: string, val: client.Hash): void { + this.setBase58(key, val, wasmlib.TYPE_HASH); + } + + setInt8(key: string, val: client.Int8): void { + let bytes = Buffer.alloc(1); + bytes.writeInt8(val, 0); + this.set(key, bytes); + } + + setInt16(key: string, val: client.Int16): void { + let bytes = Buffer.alloc(2); + bytes.writeInt16LE(val, 0); + this.set(key, bytes); + } + + setInt32(key: string, val: client.Int32): void { + let bytes = Buffer.alloc(4); + bytes.writeInt32LE(val, 0); + this.set(key, bytes); + } + + setInt64(key: string, val: client.Int64): void { + let bytes = Buffer.alloc(8); + bytes.writeBigInt64LE(val, 0); + this.set(key, bytes); + } + + setRequestID(key: string, val: client.RequestID): void { + this.setBase58(key, val, wasmlib.TYPE_REQUEST_ID); + } + + setString(key: string, val: string): void { + this.set(key, Buffer.from(val)); + } + + setUint8(key: string, val: client.Uint8): void { + let bytes = Buffer.alloc(1); + bytes.writeUInt8(val, 0); + this.set(key, bytes); + } + + setUint16(key: string, val: client.Uint16): void { + let bytes = Buffer.alloc(2); + bytes.writeUInt16LE(val, 0); + this.set(key, bytes); + } + + setUint32(key: string, val: client.Uint32): void { + let bytes = Buffer.alloc(4); + bytes.writeUInt32LE(val, 0); + this.set(key, bytes); + } + + setUint64(key: string, val: client.Uint64): void { + let bytes = Buffer.alloc(8); + bytes.writeBigUInt64LE(val, 0); + this.set(key, bytes); + } + + // Encode returns this byte array that encodes the Arguments as follows: + // Sort all keys in ascending order (very important, because this data + // will be part of the data that will be signed, so the order needs to + // be 100% deterministic). Then emit this 2-byte argument count. + // Next for each argument emit this 2-byte key length, the key prepended + // with this minus sign, this 4-byte value length, and then the value bytes. + encode(): client.Bytes { + let keys = new Array(); + for (const key of this.args.keys()) { + keys.push(key); + } + keys.sort((lhs, rhs) => lhs.localeCompare(rhs)); + + const buf = new SimpleBufferCursor(Buffer.alloc(0)); + buf.writeUInt32LE(keys.length); + for (const key of keys) { + let keyBuf = Buffer.from("-" + key); + buf.writeUInt16LE(keyBuf.length); + buf.writeBytes(keyBuf); + let valBuf = this.args.get(key); + buf.writeUInt32LE(valBuf.length); + buf.writeBytes(valBuf); + } + return buf.buffer; + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/buffer/index.d.ts b/packages/vm/wasmlib/ts/wasmlib/client/buffer/index.d.ts new file mode 100644 index 0000000000..99764ab071 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/buffer/index.d.ts @@ -0,0 +1,194 @@ +export class Buffer extends Uint8Array { + length: number + write(string: string, offset?: number, length?: number, encoding?: string): number; + toString(encoding?: string, start?: number, end?: number): string; + toJSON(): { type: 'Buffer', data: any[] }; + equals(otherBuffer: Buffer): boolean; + compare(otherBuffer: Uint8Array, targetStart?: number, targetEnd?: number, sourceStart?: number, sourceEnd?: number): number; + copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; + slice(start?: number, end?: number): Buffer; + writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readUInt8(offset: number, noAssert?: boolean): number; + readUInt16LE(offset: number, noAssert?: boolean): number; + readUInt16BE(offset: number, noAssert?: boolean): number; + readUInt32LE(offset: number, noAssert?: boolean): number; + readUInt32BE(offset: number, noAssert?: boolean): number; + readBigUInt64LE(offset: number): bigint; + readBigUInt64BE(offset: number): bigint; + readInt8(offset: number, noAssert?: boolean): number; + readInt16LE(offset: number, noAssert?: boolean): number; + readInt16BE(offset: number, noAssert?: boolean): number; + readInt32LE(offset: number, noAssert?: boolean): number; + readInt32BE(offset: number, noAssert?: boolean): number; + readBigInt64LE(offset: number): bigint; + readBigInt64BE(offset: number): bigint; + readFloatLE(offset: number, noAssert?: boolean): number; + readFloatBE(offset: number, noAssert?: boolean): number; + readDoubleLE(offset: number, noAssert?: boolean): number; + readDoubleBE(offset: number, noAssert?: boolean): number; + reverse(): this; + swap16(): Buffer; + swap32(): Buffer; + swap64(): Buffer; + writeUInt8(value: number, offset: number, noAssert?: boolean): number; + writeUInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeBigUInt64LE(value: bigint, offset: number): number; + writeBigUInt64BE(value: bigint, offset: number): number; + writeInt8(value: number, offset: number, noAssert?: boolean): number; + writeInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeBigInt64LE(value: bigint, offset: number): number; + writeBigInt64BE(value: bigint, offset: number): number; + writeFloatLE(value: number, offset: number, noAssert?: boolean): number; + writeFloatBE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleLE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleBE(value: number, offset: number, noAssert?: boolean): number; + fill(value: any, offset?: number, end?: number): this; + indexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number; + lastIndexOf(value: string | number | Buffer, byteOffset?: number, encoding?: string): number; + includes(value: string | number | Buffer, byteOffset?: number, encoding?: string): boolean; + + /** + * Allocates a new buffer containing the given {str}. + * + * @param str String to store in buffer. + * @param encoding encoding to use, optional. Default is 'utf8' + */ + constructor(str: string, encoding?: string); + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + */ + constructor(size: number); + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + constructor(array: Uint8Array); + /** + * Produces a Buffer backed by the same allocated memory as + * the given {ArrayBuffer}. + * + * + * @param arrayBuffer The ArrayBuffer with which to share memory. + */ + constructor(arrayBuffer: ArrayBuffer); + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + constructor(array: any[]); + /** + * Copies the passed {buffer} data onto a new {Buffer} instance. + * + * @param buffer The buffer to copy. + */ + constructor(buffer: Buffer); + prototype: Buffer; + /** + * Allocates a new Buffer using an {array} of octets. + * + * @param array + */ + static from(array: any[]): Buffer; + /** + * When passed a reference to the .buffer property of a TypedArray instance, + * the newly created Buffer will share the same allocated memory as the TypedArray. + * The optional {byteOffset} and {length} arguments specify a memory range + * within the {arrayBuffer} that will be shared by the Buffer. + * + * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer() + * @param byteOffset + * @param length + */ + static from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer; + /** + * Copies the passed {buffer} data onto a new Buffer instance. + * + * @param buffer + */ + static from(buffer: Buffer | Uint8Array): Buffer; + /** + * Creates a new Buffer containing the given JavaScript string {str}. + * If provided, the {encoding} parameter identifies the character encoding. + * If not provided, {encoding} defaults to 'utf8'. + * + * @param str + */ + static from(str: string, encoding?: string): Buffer; + /** + * Returns true if {obj} is a Buffer + * + * @param obj object to test. + */ + static isBuffer(obj: any): obj is Buffer; + /** + * Returns true if {encoding} is a valid encoding argument. + * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' + * + * @param encoding string to test. + */ + static isEncoding(encoding: string): boolean; + /** + * Gives the actual byte length of a string. encoding defaults to 'utf8'. + * This is not the same as String.prototype.length since that returns the number of characters in a string. + * + * @param string string to test. + * @param encoding encoding used to evaluate (defaults to 'utf8') + */ + static byteLength(string: string, encoding?: string): number; + /** + * Returns a buffer which is the result of concatenating all the buffers in the list together. + * + * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer. + * If the list has exactly one item, then the first item of the list is returned. + * If the list has more than one item, then a new Buffer is created. + * + * @param list An array of Buffer objects to concatenate + * @param totalLength Total length of the buffers when concatenated. + * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly. + */ + static concat(list: Uint8Array[], totalLength?: number): Buffer; + /** + * The same as buf1.compare(buf2). + */ + static compare(buf1: Uint8Array, buf2: Uint8Array): number; + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + * @param fill if specified, buffer will be initialized by calling buf.fill(fill). + * If parameter is omitted, buffer will be filled with zeros. + * @param encoding encoding used for call to buf.fill while initializing + */ + static alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer; + /** + * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents + * of the newly created Buffer are unknown and may contain sensitive data. + * + * @param size count of octets to allocate + */ + static allocUnsafe(size: number): Buffer; + /** + * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents + * of the newly created Buffer are unknown and may contain sensitive data. + * + * @param size count of octets to allocate + */ + static allocUnsafeSlow(size: number): Buffer; +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/buffer/index.js b/packages/vm/wasmlib/ts/wasmlib/client/buffer/index.js new file mode 100644 index 0000000000..a17a05b861 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/buffer/index.js @@ -0,0 +1,2115 @@ +/* eslint-disable no-unused-vars */ +/* eslint-disable no-constant-condition */ +/* eslint-disable no-undef */ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +/* eslint-disable no-proto */ + +'use strict' + +const base64 = require('base64-js') +const ieee754 = require('ieee754') +const customInspectSymbol = + (typeof Symbol === 'function' && typeof Symbol['for'] === 'function') // eslint-disable-line dot-notation + ? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation + : null + +exports.Buffer = Buffer +exports.SlowBuffer = SlowBuffer +exports.INSPECT_MAX_BYTES = 50 + +const K_MAX_LENGTH = 0x7fffffff +exports.kMaxLength = K_MAX_LENGTH + +/** + * If `Buffer.TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Print warning and recommend using `buffer` v4.x which has an Object + * implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * We report that the browser does not support typed arrays if the are not subclassable + * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array` + * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support + * for __proto__ and has a buggy typed array implementation. + */ +Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport() + +if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' && + typeof console.error === 'function') { + console.error( + 'This browser lacks typed array (Uint8Array) support which is required by ' + + '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.' + ) +} + +function typedArraySupport() { + // Can typed array instances can be augmented? + try { + const arr = new Uint8Array(1) + const proto = { foo: function () { return 42 } } + Object.setPrototypeOf(proto, Uint8Array.prototype) + Object.setPrototypeOf(arr, proto) + return arr.foo() === 42 + } catch (e) { + return false + } +} + +Object.defineProperty(Buffer.prototype, 'parent', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.buffer + } +}) + +Object.defineProperty(Buffer.prototype, 'offset', { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) return undefined + return this.byteOffset + } +}) + +function createBuffer(length) { + if (length > K_MAX_LENGTH) { + throw new RangeError('The value "' + length + '" is invalid for option "size"') + } + // Return an augmented `Uint8Array` instance + const buf = new Uint8Array(length) + Object.setPrototypeOf(buf, Buffer.prototype) + return buf +} + +/** + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. + * + * The `Uint8Array` prototype remains unmodified. + */ + +function Buffer(arg, encodingOrOffset, length) { + // Common case. + if (typeof arg === 'number') { + if (typeof encodingOrOffset === 'string') { + throw new TypeError( + 'The "string" argument must be of type string. Received type number' + ) + } + return allocUnsafe(arg) + } + return from(arg, encodingOrOffset, length) +} + +Buffer.poolSize = 8192 // not used by this implementation + +function from(value, encodingOrOffset, length) { + if (typeof value === 'string') { + return fromString(value, encodingOrOffset) + } + + if (ArrayBuffer.isView(value)) { + return fromArrayView(value) + } + + if (value == null) { + throw new TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) + } + + if (isInstance(value, ArrayBuffer) || + (value && isInstance(value.buffer, ArrayBuffer))) { + return fromArrayBuffer(value, encodingOrOffset, length) + } + + if (typeof SharedArrayBuffer !== 'undefined' && + (isInstance(value, SharedArrayBuffer) || + (value && isInstance(value.buffer, SharedArrayBuffer)))) { + return fromArrayBuffer(value, encodingOrOffset, length) + } + + if (typeof value === 'number') { + throw new TypeError( + 'The "value" argument must not be of type number. Received type number' + ) + } + + const valueOf = value.valueOf && value.valueOf() + if (valueOf != null && valueOf !== value) { + return Buffer.from(valueOf, encodingOrOffset, length) + } + + const b = fromObject(value) + if (b) return b + + if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null && + typeof value[Symbol.toPrimitive] === 'function') { + return Buffer.from(value[Symbol.toPrimitive]('string'), encodingOrOffset, length) + } + + throw new TypeError( + 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' + + 'or Array-like Object. Received type ' + (typeof value) + ) +} + +/** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ +Buffer.from = function (value, encodingOrOffset, length) { + return from(value, encodingOrOffset, length) +} + +// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug: +// https://github.com/feross/buffer/pull/148 +Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype) +Object.setPrototypeOf(Buffer, Uint8Array) + +function assertSize(size) { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be of type number') + } else if (size < 0) { + throw new RangeError('The value "' + size + '" is invalid for option "size"') + } +} + +function alloc(size, fill, encoding) { + assertSize(size) + if (size <= 0) { + return createBuffer(size) + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpreted as a start offset. + return typeof encoding === 'string' + ? createBuffer(size).fill(fill, encoding) + : createBuffer(size).fill(fill) + } + return createBuffer(size) +} + +/** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ +Buffer.alloc = function (size, fill, encoding) { + return alloc(size, fill, encoding) +} + +function allocUnsafe(size) { + assertSize(size) + return createBuffer(size < 0 ? 0 : checked(size) | 0) +} + +/** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ +Buffer.allocUnsafe = function (size) { + return allocUnsafe(size) +} +/** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ +Buffer.allocUnsafeSlow = function (size) { + return allocUnsafe(size) +} + +function fromString(string, encoding) { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8' + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + + const length = byteLength(string, encoding) | 0 + let buf = createBuffer(length) + + const actual = buf.write(string, encoding) + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + buf = buf.slice(0, actual) + } + + return buf +} + +function fromArrayLike(array) { + const length = array.length < 0 ? 0 : checked(array.length) | 0 + const buf = createBuffer(length) + for (let i = 0; i < length; i += 1) { + buf[i] = array[i] & 255 + } + return buf +} + +function fromArrayView(arrayView) { + if (isInstance(arrayView, Uint8Array)) { + const copy = new Uint8Array(arrayView) + return fromArrayBuffer(copy.buffer, copy.byteOffset, copy.byteLength) + } + return fromArrayLike(arrayView) +} + +function fromArrayBuffer(array, byteOffset, length) { + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('"offset" is outside of buffer bounds') + } + + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('"length" is outside of buffer bounds') + } + + let buf + if (byteOffset === undefined && length === undefined) { + buf = new Uint8Array(array) + } else if (length === undefined) { + buf = new Uint8Array(array, byteOffset) + } else { + buf = new Uint8Array(array, byteOffset, length) + } + + // Return an augmented `Uint8Array` instance + Object.setPrototypeOf(buf, Buffer.prototype) + + return buf +} + +function fromObject(obj) { + if (Buffer.isBuffer(obj)) { + const len = checked(obj.length) | 0 + const buf = createBuffer(len) + + if (buf.length === 0) { + return buf + } + + obj.copy(buf, 0, 0, len) + return buf + } + + if (obj.length !== undefined) { + if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) { + return createBuffer(0) + } + return fromArrayLike(obj) + } + + if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + return fromArrayLike(obj.data) + } +} + +function checked(length) { + // Note: cannot use `length < K_MAX_LENGTH` here because that fails when + // length is NaN (which is otherwise coerced to zero.) + if (length >= K_MAX_LENGTH) { + throw new RangeError('Attempt to allocate Buffer larger than maximum ' + + 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes') + } + return length | 0 +} + +function SlowBuffer(length) { + if (+length != length) { // eslint-disable-line eqeqeq + length = 0 + } + return Buffer.alloc(+length) +} + +Buffer.isBuffer = function isBuffer(b) { + return b != null && b._isBuffer === true && + b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false +} + +Buffer.compare = function compare(a, b) { + if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength) + if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength) + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError( + 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array' + ) + } + + if (a === b) return 0 + + let x = a.length + let y = b.length + + for (let i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i] + y = b[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +Buffer.isEncoding = function isEncoding(encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'latin1': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.concat = function concat(list, length) { + if (!Array.isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + + if (list.length === 0) { + return Buffer.alloc(0) + } + + let i + if (length === undefined) { + length = 0 + for (i = 0; i < list.length; ++i) { + length += list[i].length + } + } + + const buffer = Buffer.allocUnsafe(length) + let pos = 0 + for (i = 0; i < list.length; ++i) { + let buf = list[i] + if (isInstance(buf, Uint8Array)) { + if (pos + buf.length > buffer.length) { + if (!Buffer.isBuffer(buf)) { + buf = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength) + } + buf.copy(buffer, pos) + } else { + Uint8Array.prototype.set.call( + buffer, + buf, + pos + ) + } + } else if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } else { + buf.copy(buffer, pos) + } + pos += buf.length + } + return buffer +} + +function byteLength(string, encoding) { + if (Buffer.isBuffer(string)) { + return string.length + } + if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) { + return string.byteLength + } + if (typeof string !== 'string') { + throw new TypeError( + 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' + + 'Received type ' + typeof string + ) + } + + const len = string.length + const mustMatch = (arguments.length > 2 && arguments[2] === true) + if (!mustMatch && len === 0) return 0 + + // Use a for loop to avoid recursion + let loweredCase = false + for (; ;) { + switch (encoding) { + case 'ascii': + case 'latin1': + case 'binary': + return len + case 'utf8': + case 'utf-8': + return utf8ToBytes(string).length + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return len * 2 + case 'hex': + return len >>> 1 + case 'base64': + return base64ToBytes(string).length + default: + if (loweredCase) { + return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8 + } + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} +Buffer.byteLength = byteLength + +function slowToString(encoding, start, end) { + let loweredCase = false + + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0 + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return '' + } + + if (end === undefined || end > this.length) { + end = this.length + } + + if (end <= 0) { + return '' + } + + // Force coercion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0 + start >>>= 0 + + if (end <= start) { + return '' + } + + if (!encoding) encoding = 'utf8' + + while (true) { + switch (encoding) { + case 'hex': + return hexSlice(this, start, end) + + case 'utf8': + case 'utf-8': + return utf8Slice(this, start, end) + + case 'ascii': + return asciiSlice(this, start, end) + + case 'latin1': + case 'binary': + return latin1Slice(this, start, end) + + case 'base64': + return base64Slice(this, start, end) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return utf16leSlice(this, start, end) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = (encoding + '').toLowerCase() + loweredCase = true + } + } +} + +// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package) +// to detect a Buffer instance. It's not possible to use `instanceof Buffer` +// reliably in a browserify context because there could be multiple different +// copies of the 'buffer' package in use. This method works even for Buffer +// instances that were created from another copy of the `buffer` package. +// See: https://github.com/feross/buffer/issues/154 +Buffer.prototype._isBuffer = true + +function swap(b, n, m) { + const i = b[n] + b[n] = b[m] + b[m] = i +} + +Buffer.prototype.swap16 = function swap16() { + const len = this.length + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits') + } + for (let i = 0; i < len; i += 2) { + swap(this, i, i + 1) + } + return this +} + +Buffer.prototype.swap32 = function swap32() { + const len = this.length + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits') + } + for (let i = 0; i < len; i += 4) { + swap(this, i, i + 3) + swap(this, i + 1, i + 2) + } + return this +} + +Buffer.prototype.swap64 = function swap64() { + const len = this.length + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits') + } + for (let i = 0; i < len; i += 8) { + swap(this, i, i + 7) + swap(this, i + 1, i + 6) + swap(this, i + 2, i + 5) + swap(this, i + 3, i + 4) + } + return this +} + +Buffer.prototype.toString = function toString() { + const length = this.length + if (length === 0) return '' + if (arguments.length === 0) return utf8Slice(this, 0, length) + return slowToString.apply(this, arguments) +} + +Buffer.prototype.toLocaleString = Buffer.prototype.toString + +Buffer.prototype.equals = function equals(b) { + if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') + if (this === b) return true + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.inspect = function inspect() { + let str = '' + const max = exports.INSPECT_MAX_BYTES + str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim() + if (this.length > max) str += ' ... ' + return '' +} +if (customInspectSymbol) { + Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect +} + +Buffer.prototype.compare = function compare(target, start, end, thisStart, thisEnd) { + if (isInstance(target, Uint8Array)) { + target = Buffer.from(target, target.offset, target.byteLength) + } + if (!Buffer.isBuffer(target)) { + throw new TypeError( + 'The "target" argument must be one of type Buffer or Uint8Array. ' + + 'Received type ' + (typeof target) + ) + } + + if (start === undefined) { + start = 0 + } + if (end === undefined) { + end = target ? target.length : 0 + } + if (thisStart === undefined) { + thisStart = 0 + } + if (thisEnd === undefined) { + thisEnd = this.length + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index') + } + + if (thisStart >= thisEnd && start >= end) { + return 0 + } + if (thisStart >= thisEnd) { + return -1 + } + if (start >= end) { + return 1 + } + + start >>>= 0 + end >>>= 0 + thisStart >>>= 0 + thisEnd >>>= 0 + + if (this === target) return 0 + + let x = thisEnd - thisStart + let y = end - start + const len = Math.min(x, y) + + const thisCopy = this.slice(thisStart, thisEnd) + const targetCopy = target.slice(start, end) + + for (let i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i] + y = targetCopy[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 +} + +// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, +// OR the last index of `val` in `buffer` at offset <= `byteOffset`. +// +// Arguments: +// - buffer - a Buffer to search +// - val - a string, Buffer, or number +// - byteOffset - an index into `buffer`; will be clamped to an int32 +// - encoding - an optional encoding, relevant is val is a string +// - dir - true for indexOf, false for lastIndexOf +function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1 + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset + byteOffset = 0 + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000 + } + byteOffset = +byteOffset // Coerce to Number. + if (numberIsNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : (buffer.length - 1) + } + + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset + if (byteOffset >= buffer.length) { + if (dir) return -1 + else byteOffset = buffer.length - 1 + } else if (byteOffset < 0) { + if (dir) byteOffset = 0 + else return -1 + } + + // Normalize val + if (typeof val === 'string') { + val = Buffer.from(val, encoding) + } + + // Finally, search either indexOf (if dir is true) or lastIndexOf + if (Buffer.isBuffer(val)) { + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1 + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir) + } else if (typeof val === 'number') { + val = val & 0xFF // Search for a byte value [0-255] + if (typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + } + } + return arrayIndexOf(buffer, [val], byteOffset, encoding, dir) + } + + throw new TypeError('val must be string, number or Buffer') +} + +function arrayIndexOf(arr, val, byteOffset, encoding, dir) { + let indexSize = 1 + let arrLength = arr.length + let valLength = val.length + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase() + if (encoding === 'ucs2' || encoding === 'ucs-2' || + encoding === 'utf16le' || encoding === 'utf-16le') { + if (arr.length < 2 || val.length < 2) { + return -1 + } + indexSize = 2 + arrLength /= 2 + valLength /= 2 + byteOffset /= 2 + } + } + + function read(buf, i) { + if (indexSize === 1) { + return buf[i] + } else { + return buf.readUInt16BE(i * indexSize) + } + } + + let i + if (dir) { + let foundIndex = -1 + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { + if (foundIndex === -1) foundIndex = i + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize + } else { + if (foundIndex !== -1) i -= i - foundIndex + foundIndex = -1 + } + } + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength + for (i = byteOffset; i >= 0; i--) { + let found = true + for (let j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false + break + } + } + if (found) return i + } + } + + return -1 +} + +Buffer.prototype.includes = function includes(val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1 +} + +Buffer.prototype.indexOf = function indexOf(val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true) +} + +Buffer.prototype.lastIndexOf = function lastIndexOf(val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false) +} + +function hexWrite(buf, string, offset, length) { + offset = Number(offset) || 0 + const remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + const strLen = string.length + + if (length > strLen / 2) { + length = strLen / 2 + } + let i + for (i = 0; i < length; ++i) { + const parsed = parseInt(string.substr(i * 2, 2), 16) + if (numberIsNaN(parsed)) return i + buf[offset + i] = parsed + } + return i +} + +function utf8Write(buf, string, offset, length) { + return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) +} + +function asciiWrite(buf, string, offset, length) { + return blitBuffer(asciiToBytes(string), buf, offset, length) +} + +function base64Write(buf, string, offset, length) { + return blitBuffer(base64ToBytes(string), buf, offset, length) +} + +function ucs2Write(buf, string, offset, length) { + return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) +} + +Buffer.prototype.write = function write(string, offset, length, encoding) { + // Buffer#write(string) + if (offset === undefined) { + encoding = 'utf8' + length = this.length + offset = 0 + // Buffer#write(string, encoding) + } else if (length === undefined && typeof offset === 'string') { + encoding = offset + length = this.length + offset = 0 + // Buffer#write(string, offset[, length][, encoding]) + } else if (isFinite(offset)) { + offset = offset >>> 0 + if (isFinite(length)) { + length = length >>> 0 + if (encoding === undefined) encoding = 'utf8' + } else { + encoding = length + length = undefined + } + } else { + throw new Error( + 'Buffer.write(string, encoding, offset[, length]) is no longer supported' + ) + } + + const remaining = this.length - offset + if (length === undefined || length > remaining) length = remaining + + if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { + throw new RangeError('Attempt to write outside buffer bounds') + } + + if (!encoding) encoding = 'utf8' + + let loweredCase = false + for (; ;) { + switch (encoding) { + case 'hex': + return hexWrite(this, string, offset, length) + + case 'utf8': + case 'utf-8': + return utf8Write(this, string, offset, length) + + case 'ascii': + case 'latin1': + case 'binary': + return asciiWrite(this, string, offset, length) + + case 'base64': + // Warning: maxLength not taken into account in base64Write + return base64Write(this, string, offset, length) + + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return ucs2Write(this, string, offset, length) + + default: + if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) + encoding = ('' + encoding).toLowerCase() + loweredCase = true + } + } +} + +Buffer.prototype.toJSON = function toJSON() { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +function base64Slice(buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice(buf, start, end) { + end = Math.min(buf.length, end) + const res = [] + + let i = start + while (i < end) { + const firstByte = buf[i] + let codePoint = null + let bytesPerSequence = (firstByte > 0xEF) + ? 4 + : (firstByte > 0xDF) + ? 3 + : (firstByte > 0xBF) + ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + let secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } + + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF + } + + res.push(codePoint) + i += bytesPerSequence + } + + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +const MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray(codePoints) { + const len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + let res = '' + let i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res +} + +function asciiSlice(buf, start, end) { + let ret = '' + end = Math.min(buf.length, end) + + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i] & 0x7F) + } + return ret +} + +function latin1Slice(buf, start, end) { + let ret = '' + end = Math.min(buf.length, end) + + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function hexSlice(buf, start, end) { + const len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + let out = '' + for (let i = start; i < end; ++i) { + out += hexSliceLookupTable[buf[i]] + } + return out +} + +function utf16leSlice(buf, start, end) { + const bytes = buf.slice(start, end) + let res = '' + // If bytes.length is odd, the last 8 bits must be ignored (same as node.js) + for (let i = 0; i < bytes.length - 1; i += 2) { + res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256)) + } + return res +} + +Buffer.prototype.slice = function slice(start, end) { + const len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len + if (start < 0) start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) end = 0 + } else if (end > len) { + end = len + } + + if (end < start) end = start + + const newBuf = this.subarray(start, end) + // Return an augmented `Uint8Array` instance + Object.setPrototypeOf(newBuf, Buffer.prototype) + + return newBuf +} + +/* + * Need to make sure that buffer isn't trying to write out of bounds. + */ +function checkOffset(offset, ext, length) { + if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') + if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') +} + +Buffer.prototype.readUintLE = + Buffer.prototype.readUIntLE = function readUIntLE(offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + let val = this[offset] + let mul = 1 + let i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + + return val + } + +Buffer.prototype.readUintBE = + Buffer.prototype.readUIntBE = function readUIntBE(offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + checkOffset(offset, byteLength, this.length) + } + + let val = this[offset + --byteLength] + let mul = 1 + while (byteLength > 0 && (mul *= 0x100)) { + val += this[offset + --byteLength] * mul + } + + return val + } + +Buffer.prototype.readUint8 = + Buffer.prototype.readUInt8 = function readUInt8(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + return this[offset] + } + +Buffer.prototype.readUint16LE = + Buffer.prototype.readUInt16LE = function readUInt16LE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return this[offset] | (this[offset + 1] << 8) + } + +Buffer.prototype.readUint16BE = + Buffer.prototype.readUInt16BE = function readUInt16BE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + return (this[offset] << 8) | this[offset + 1] + } + +Buffer.prototype.readUint32LE = + Buffer.prototype.readUInt32LE = function readUInt32LE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return ((this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16)) + + (this[offset + 3] * 0x1000000) + } + +Buffer.prototype.readUint32BE = + Buffer.prototype.readUInt32BE = function readUInt32BE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] * 0x1000000) + + ((this[offset + 1] << 16) | + (this[offset + 2] << 8) | + this[offset + 3]) + } + +Buffer.prototype.readBigUInt64LE = defineBigIntMethod(function readBigUInt64LE(offset) { + offset = offset >>> 0 + validateNumber(offset, 'offset') + const first = this[offset] + const last = this[offset + 7] + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8) + } + + const lo = first + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 24 + + const hi = this[++offset] + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + last * 2 ** 24 + + return BigInt(lo) + (BigInt(hi) << BigInt(32)) +}) + +Buffer.prototype.readBigUInt64BE = defineBigIntMethod(function readBigUInt64BE(offset) { + offset = offset >>> 0 + validateNumber(offset, 'offset') + const first = this[offset] + const last = this[offset + 7] + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8) + } + + const hi = first * 2 ** 24 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + this[++offset] + + const lo = this[++offset] * 2 ** 24 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + last + + return (BigInt(hi) << BigInt(32)) + BigInt(lo) +}) + +Buffer.prototype.readIntLE = function readIntLE(offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + let val = this[offset] + let mul = 1 + let i = 0 + while (++i < byteLength && (mul *= 0x100)) { + val += this[offset + i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readIntBE = function readIntBE(offset, byteLength, noAssert) { + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) checkOffset(offset, byteLength, this.length) + + let i = byteLength + let mul = 1 + let val = this[offset + --i] + while (i > 0 && (mul *= 0x100)) { + val += this[offset + --i] * mul + } + mul *= 0x80 + + if (val >= mul) val -= Math.pow(2, 8 * byteLength) + + return val +} + +Buffer.prototype.readInt8 = function readInt8(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 1, this.length) + if (!(this[offset] & 0x80)) return (this[offset]) + return ((0xff - this[offset] + 1) * -1) +} + +Buffer.prototype.readInt16LE = function readInt16LE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + const val = this[offset] | (this[offset + 1] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt16BE = function readInt16BE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 2, this.length) + const val = this[offset + 1] | (this[offset] << 8) + return (val & 0x8000) ? val | 0xFFFF0000 : val +} + +Buffer.prototype.readInt32LE = function readInt32LE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset]) | + (this[offset + 1] << 8) | + (this[offset + 2] << 16) | + (this[offset + 3] << 24) +} + +Buffer.prototype.readInt32BE = function readInt32BE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + + return (this[offset] << 24) | + (this[offset + 1] << 16) | + (this[offset + 2] << 8) | + (this[offset + 3]) +} + +Buffer.prototype.readBigInt64LE = defineBigIntMethod(function readBigInt64LE(offset) { + offset = offset >>> 0 + validateNumber(offset, 'offset') + const first = this[offset] + const last = this[offset + 7] + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8) + } + + const val = this[offset + 4] + + this[offset + 5] * 2 ** 8 + + this[offset + 6] * 2 ** 16 + + (last << 24) // Overflow + + return (BigInt(val) << BigInt(32)) + + BigInt(first + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 24) +}) + +Buffer.prototype.readBigInt64BE = defineBigIntMethod(function readBigInt64BE(offset) { + offset = offset >>> 0 + validateNumber(offset, 'offset') + const first = this[offset] + const last = this[offset + 7] + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 8) + } + + const val = (first << 24) + // Overflow + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + this[++offset] + + return (BigInt(val) << BigInt(32)) + + BigInt(this[++offset] * 2 ** 24 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + last) +}) + +Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, true, 23, 4) +} + +Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 4, this.length) + return ieee754.read(this, offset, false, 23, 4) +} + +Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, true, 52, 8) +} + +Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { + offset = offset >>> 0 + if (!noAssert) checkOffset(offset, 8, this.length) + return ieee754.read(this, offset, false, 52, 8) +} + +function checkInt(buf, value, offset, ext, max, min) { + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') + if (offset + ext > buf.length) throw new RangeError('Index out of range') +} + +Buffer.prototype.writeUintLE = + Buffer.prototype.writeUIntLE = function writeUIntLE(value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + const maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + let mul = 1 + let i = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength + } + +Buffer.prototype.writeUintBE = + Buffer.prototype.writeUIntBE = function writeUIntBE(value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + byteLength = byteLength >>> 0 + if (!noAssert) { + const maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } + + let i = byteLength - 1 + let mul = 1 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + this[offset + i] = (value / mul) & 0xFF + } + + return offset + byteLength + } + +Buffer.prototype.writeUint8 = + Buffer.prototype.writeUInt8 = function writeUInt8(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) + this[offset] = (value & 0xff) + return offset + 1 + } + +Buffer.prototype.writeUint16LE = + Buffer.prototype.writeUInt16LE = function writeUInt16LE(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 + } + +Buffer.prototype.writeUint16BE = + Buffer.prototype.writeUInt16BE = function writeUInt16BE(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 + } + +Buffer.prototype.writeUint32LE = + Buffer.prototype.writeUInt32LE = function writeUInt32LE(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset + 3] = (value >>> 24) + this[offset + 2] = (value >>> 16) + this[offset + 1] = (value >>> 8) + this[offset] = (value & 0xff) + return offset + 4 + } + +Buffer.prototype.writeUint32BE = + Buffer.prototype.writeUInt32BE = function writeUInt32BE(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 + } + +function wrtBigUInt64LE(buf, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7) + + let lo = Number(value & BigInt(0xffffffff)) + + buf[offset++] = lo + lo = lo >> 8 + buf[offset++] = lo + lo = lo >> 8 + buf[offset++] = lo + lo = lo >> 8 + buf[offset++] = lo + + let hi = Number(value >> (BigInt(32) & BigInt(0xffffffff))) + + buf[offset++] = hi + hi = hi >> 8 + buf[offset++] = hi + hi = hi >> 8 + buf[offset++] = hi + hi = hi >> 8 + buf[offset++] = hi + + return offset +} + +function wrtBigUInt64BE(buf, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7) + + let lo = Number(value & BigInt(0xffffffff)) + buf[offset + 7] = lo + lo = lo >> 8 + buf[offset + 6] = lo + lo = lo >> 8 + buf[offset + 5] = lo + lo = lo >> 8 + buf[offset + 4] = lo + let hi = Number(value >> (BigInt(32) & BigInt(0xffffffff))) + buf[offset + 3] = hi + hi = hi >> 8 + buf[offset + 2] = hi + hi = hi >> 8 + buf[offset + 1] = hi + hi = hi >> 8 + buf[offset] = hi + return offset + 8 +} + +Buffer.prototype.writeBigUInt64LE = defineBigIntMethod(function writeBigUInt64LE(value, offset = 0) { + return wrtBigUInt64LE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff')) +}) + +Buffer.prototype.writeBigUInt64BE = defineBigIntMethod(function writeBigUInt64BE(value, offset = 0) { + return wrtBigUInt64BE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff')) +}) + +Buffer.prototype.writeIntLE = function writeIntLE(value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + const limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + let i = 0 + let mul = 1 + let sub = 0 + this[offset] = value & 0xFF + while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeIntBE = function writeIntBE(value, offset, byteLength, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + const limit = Math.pow(2, (8 * byteLength) - 1) + + checkInt(this, value, offset, byteLength, limit - 1, -limit) + } + + let i = byteLength - 1 + let mul = 1 + let sub = 0 + this[offset + i] = value & 0xFF + while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1 + } + this[offset + i] = ((value / mul) >> 0) - sub & 0xFF + } + + return offset + byteLength +} + +Buffer.prototype.writeInt8 = function writeInt8(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) + if (value < 0) value = 0xff + value + 1 + this[offset] = (value & 0xff) + return offset + 1 +} + +Buffer.prototype.writeInt16LE = function writeInt16LE(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + return offset + 2 +} + +Buffer.prototype.writeInt16BE = function writeInt16BE(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) + this[offset] = (value >>> 8) + this[offset + 1] = (value & 0xff) + return offset + 2 +} + +Buffer.prototype.writeInt32LE = function writeInt32LE(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + this[offset] = (value & 0xff) + this[offset + 1] = (value >>> 8) + this[offset + 2] = (value >>> 16) + this[offset + 3] = (value >>> 24) + return offset + 4 +} + +Buffer.prototype.writeInt32BE = function writeInt32BE(value, offset, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) + if (value < 0) value = 0xffffffff + value + 1 + this[offset] = (value >>> 24) + this[offset + 1] = (value >>> 16) + this[offset + 2] = (value >>> 8) + this[offset + 3] = (value & 0xff) + return offset + 4 +} + +Buffer.prototype.writeBigInt64LE = defineBigIntMethod(function writeBigInt64LE(value, offset = 0) { + return wrtBigUInt64LE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff')) +}) + +Buffer.prototype.writeBigInt64BE = defineBigIntMethod(function writeBigInt64BE(value, offset = 0) { + return wrtBigUInt64BE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff')) +}) + +function checkIEEE754(buf, value, offset, ext, max, min) { + if (offset + ext > buf.length) throw new RangeError('Index out of range') + if (offset < 0) throw new RangeError('Index out of range') +} + +function writeFloat(buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function writeFloatLE(value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function writeFloatBE(value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble(buf, value, offset, littleEndian, noAssert) { + value = +value + offset = offset >>> 0 + if (!noAssert) { + checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function writeDoubleLE(value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function writeDoubleBE(value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function copy(target, targetStart, start, end) { + if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer') + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (targetStart >= target.length) targetStart = target.length + if (!targetStart) targetStart = 0 + if (end > 0 && end < start) end = start + + // Copy 0 bytes; we're done + if (end === start) return 0 + if (target.length === 0 || this.length === 0) return 0 + + // Fatal error conditions + if (targetStart < 0) { + throw new RangeError('targetStart out of bounds') + } + if (start < 0 || start >= this.length) throw new RangeError('Index out of range') + if (end < 0) throw new RangeError('sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) end = this.length + if (target.length - targetStart < end - start) { + end = target.length - targetStart + start + } + + const len = end - start + + if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') { + // Use built-in when available, missing from IE11 + this.copyWithin(targetStart, start, end) + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(start, end), + targetStart + ) + } + + return len +} + +// Usage: +// buffer.fill(number[, offset[, end]]) +// buffer.fill(buffer[, offset[, end]]) +// buffer.fill(string[, offset[, end]][, encoding]) +Buffer.prototype.fill = function fill(val, start, end, encoding) { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start + start = 0 + end = this.length + } else if (typeof end === 'string') { + encoding = end + end = this.length + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string') + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + if (val.length === 1) { + const code = val.charCodeAt(0) + if ((encoding === 'utf8' && code < 128) || + encoding === 'latin1') { + // Fast path: If `val` fits into a single byte, use that numeric value. + val = code + } + } + } else if (typeof val === 'number') { + val = val & 255 + } else if (typeof val === 'boolean') { + val = Number(val) + } + + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index') + } + + if (end <= start) { + return this + } + + start = start >>> 0 + end = end === undefined ? this.length : end >>> 0 + + if (!val) val = 0 + + let i + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val + } + } else { + const bytes = Buffer.isBuffer(val) + ? val + : Buffer.from(val, encoding) + const len = bytes.length + if (len === 0) { + throw new TypeError('The value "' + val + + '" is invalid for argument "value"') + } + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len] + } + } + + return this +} + +// CUSTOM ERRORS +// ============= + +// Simplified versions from Node, changed for Buffer-only usage +const errors = { } +function E(sym, getMessage, Base) { + errors[sym] = class NodeError extends Base { + constructor() { + super() + + Object.defineProperty(this, 'message', { + value: getMessage.apply(this, arguments), + writable: true, + configurable: true + }) + + // Add the error code to the name to include it in the stack trace. + this.name = `${this.name} [${sym}]` + // Access the stack to generate the error message including the error code + // from the name. + this.stack // eslint-disable-line no-unused-expressions + // Reset the name to the actual name. + delete this.name + } + + get code() { + return sym + } + + set code(value) { + Object.defineProperty(this, 'code', { + configurable: true, + enumerable: true, + value, + writable: true + }) + } + + toString() { + return `${this.name} [${sym}]: ${this.message}` + } + } +} + +E('ERR_BUFFER_OUT_OF_BOUNDS', + function (name) { + if (name) { + return `${name} is outside of buffer bounds` + } + + return 'Attempt to access memory outside buffer bounds' + }, RangeError) +E('ERR_INVALID_ARG_TYPE', + function (name, actual) { + return `The "${name}" argument must be of type number. Received type ${typeof actual}` + }, TypeError) +E('ERR_OUT_OF_RANGE', + function (str, range, input) { + let msg = `The value of "${str}" is out of range.` + let received = input + if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { + received = addNumericalSeparator(String(input)) + } else if (typeof input === 'bigint') { + received = String(input) + if (input > BigInt(2) ** BigInt(32) || input < -(BigInt(2) ** BigInt(32))) { + received = addNumericalSeparator(received) + } + received += 'n' + } + msg += ` It must be ${range}. Received ${received}` + return msg + }, RangeError) + +function addNumericalSeparator(val) { + let res = '' + let i = val.length + const start = val[0] === '-' ? 1 : 0 + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}` + } + return `${val.slice(0, i)}${res}` +} + +// CHECK FUNCTIONS +// =============== + +function checkBounds(buf, offset, byteLength) { + validateNumber(offset, 'offset') + if (buf[offset] === undefined || buf[offset + byteLength] === undefined) { + boundsError(offset, buf.length - (byteLength + 1)) + } +} + +function checkIntBI(value, min, max, buf, offset, byteLength) { + if (value > max || value < min) { + const n = typeof min === 'bigint' ? 'n' : '' + let range + if (byteLength > 3) { + if (min === 0 || min === BigInt(0)) { + range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}` + } else { + range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and < 2 ** ` + + `${(byteLength + 1) * 8 - 1}${n}` + } + } else { + range = `>= ${min}${n} and <= ${max}${n}` + } + throw new errors.ERR_OUT_OF_RANGE('value', range, value) + } + checkBounds(buf, offset, byteLength) +} + +function validateNumber(value, name) { + if (typeof value !== 'number') { + throw new errors.ERR_INVALID_ARG_TYPE(name, 'number', value) + } +} + +function boundsError(value, length, type) { + if (Math.floor(value) !== value) { + validateNumber(value, type) + throw new errors.ERR_OUT_OF_RANGE(type || 'offset', 'an integer', value) + } + + if (length < 0) { + throw new errors.ERR_BUFFER_OUT_OF_BOUNDS() + } + + throw new errors.ERR_OUT_OF_RANGE(type || 'offset', + `>= ${type ? 1 : 0} and <= ${length}`, + value) +} + +// HELPER FUNCTIONS +// ================ + +const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g + +function base64clean(str) { + // Node takes equal signs as end of the Base64 encoding + str = str.split('=')[0] + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = str.trim().replace(INVALID_BASE64_RE, '') + // Node converts strings with length < 2 to '' + if (str.length < 2) return '' + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function utf8ToBytes(string, units) { + units = units || Infinity + let codePoint + const length = string.length + let leadSurrogate = null + const bytes = [] + + for (let i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i) + + // is surrogate component + if (codePoint > 0xD7FF && codePoint < 0xE000) { + // last char was a lead + if (!leadSurrogate) { + // no lead yet + if (codePoint > 0xDBFF) { + // unexpected trail + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } else if (i + 1 === length) { + // unpaired lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + continue + } + + // valid lead + leadSurrogate = codePoint + + continue + } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 + } else if (leadSurrogate) { + // valid bmp char, but last char was a lead + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + } + + leadSurrogate = null + + // encode utf8 + if (codePoint < 0x80) { + if ((units -= 1) < 0) break + bytes.push(codePoint) + } else if (codePoint < 0x800) { + if ((units -= 2) < 0) break + bytes.push( + codePoint >> 0x6 | 0xC0, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x10000) { + if ((units -= 3) < 0) break + bytes.push( + codePoint >> 0xC | 0xE0, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else if (codePoint < 0x110000) { + if ((units -= 4) < 0) break + bytes.push( + codePoint >> 0x12 | 0xF0, + codePoint >> 0xC & 0x3F | 0x80, + codePoint >> 0x6 & 0x3F | 0x80, + codePoint & 0x3F | 0x80 + ) + } else { + throw new Error('Invalid code point') + } + } + + return bytes +} + +function asciiToBytes(str) { + const byteArray = [] + for (let i = 0; i < str.length; ++i) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes(str, units) { + let c, hi, lo + const byteArray = [] + for (let i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) break + + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes(str) { + return base64.toByteArray(base64clean(str)) +} + +function blitBuffer(src, dst, offset, length) { + let i + for (i = 0; i < length; ++i) { + if ((i + offset >= dst.length) || (i >= src.length)) break + dst[i + offset] = src[i] + } + return i +} + +// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass +// the `instanceof` check but they should be treated as of that type. +// See: https://github.com/feross/buffer/issues/166 +function isInstance(obj, type) { + return obj instanceof type || + (obj != null && obj.constructor != null && obj.constructor.name != null && + obj.constructor.name === type.name) +} +function numberIsNaN(obj) { + // For IE11 support + return obj !== obj // eslint-disable-line no-self-compare +} + +// Create lookup table for `toString('hex')` +// See: https://github.com/feross/buffer/issues/219 +const hexSliceLookupTable = (function () { + const alphabet = '0123456789abcdef' + const table = new Array(256) + for (let i = 0; i < 16; ++i) { + const i16 = i * 16 + for (let j = 0; j < 16; ++j) { + table[i16 + j] = alphabet[i] + alphabet[j] + } + } + return table +})() + +// Return not function with Error if BigInt not supported +function defineBigIntMethod(fn) { + return typeof BigInt === 'undefined' ? BufferBigIntNotDefined : fn +} + +function BufferBigIntNotDefined() { + throw new Error('BigInt not supported') +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/buffer/readme.md b/packages/vm/wasmlib/ts/wasmlib/client/buffer/readme.md new file mode 100644 index 0000000000..0c78405f8c --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/buffer/readme.md @@ -0,0 +1,3 @@ +From https://github.com/feross/buffer/ + +Required minor changes to work with TypeScript properly and is not published yet. diff --git a/packages/vm/wasmlib/ts/wasmlib/client/config.dev.js b/packages/vm/wasmlib/ts/wasmlib/client/config.dev.js new file mode 100644 index 0000000000..fe795a7f20 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/config.dev.js @@ -0,0 +1,10 @@ +module.exports = { + // Either set a default seed, or set it to undefined to generate a new seed each page load + seed: undefined, + waspWebSocketUrl: 'ws://127.0.0.1:9090/chain/%chainId/ws', + waspApiUrl: 'http://127.0.0.1:9090', + goshimmerApiUrl: 'http://127.0.0.1:8080', + chainId: 'pG9BvsC7h1tYPtqQityhH1qjCaN8A65mGWoxDQgSSbRt', + googleAnalytics: undefined, + contractName: 'fairroulette' +}; diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/base58.ts b/packages/vm/wasmlib/ts/wasmlib/client/crypto/base58.ts new file mode 100644 index 0000000000..d81ed61df5 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/crypto/base58.ts @@ -0,0 +1,148 @@ +import { Buffer } from '../buffer'; + +export class Base58 { + private static readonly ALPHABET: string = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + private static readonly ALPHABET_MAP: { [id: string]: number } = {}; + + /** + * Encode a buffer to base58. + * @param buffer The buffer to encode as base 58. + * @returns The encoded data as a string. + */ + public static encode(buffer: Buffer): string { + if (!buffer || buffer.length == 0) { + return ''; + } + let i = 0; + let j; + let carry; + const digits = [0]; + while (i < buffer.length) { + j = 0; + while (j < digits.length) { + digits[j] <<= 8; + j++; + } + digits[0] += buffer[i]; + carry = 0; + j = 0; + while (j < digits.length) { + digits[j] += carry; + carry = (digits[j] / 58) | 0; + digits[j] %= 58; + ++j; + } + while (carry) { + digits.push(carry % 58); + carry = (carry / 58) | 0; + } + i++; + } + i = 0; + while (buffer[i] == 0 && i < buffer.length - 1) { + digits.push(0); + i++; + } + return digits + .reverse() + .map((digit) => { + return Base58.ALPHABET[digit]; + }) + .join(''); + } + + /** + * Decode a base58 string to a buffer. + * @param encoded The buffer to encode as base 58. + * @returns The encoded data as a string. + */ + public static decode(encoded: string): Buffer { + if (!encoded || encoded.length == 0) { + return Buffer.from(''); + } + Base58.buildMap(); + let i = 0; + let j; + let c; + let carry; + const bytes = [0]; + i = 0; + while (i < encoded.length) { + c = encoded[i]; + if (!(c in Base58.ALPHABET_MAP)) { + throw new Error(`Character '${c}' is not in the Base58 alphabet.`); + } + j = 0; + while (j < bytes.length) { + bytes[j] *= 58; + j++; + } + bytes[0] += Base58.ALPHABET_MAP[c]; + carry = 0; + j = 0; + while (j < bytes.length) { + bytes[j] += carry; + carry = bytes[j] >> 8; + bytes[j] &= 0xff; + ++j; + } + while (carry) { + bytes.push(carry & 0xff); + carry >>= 8; + } + i++; + } + i = 0; + while (encoded[i] == '1' && i < encoded.length - 1) { + bytes.push(0); + i++; + } + return Buffer.from(bytes.reverse()); + } + + /** + * Is the encoded string valid base58. + * @param encoded The encoded string as base 58. + * @returns True is the characters are valid. + */ + public static isValid(encoded?: string): boolean { + if (!encoded) { + return false; + } + Base58.buildMap(); + for (const ch of encoded) { + if (!(ch in Base58.ALPHABET_MAP)) { + return false; + } + } + return true; + } + + /** + * Concatenate 2 base58 strings. + * @param id1 The first id. + * @param id2 The second id. + * @returns The concatenated ids. + */ + public static concat(id1: string, id2: string): string { + const b1 = Base58.decode(id1); + const b2 = Base58.decode(id2); + const combined = Buffer.alloc(b1.length + b2.length); + combined.set(b1, 0); + combined.set(b2, b1.length); + return Base58.encode(combined); + } + + /** + * Build the reverse lookup map. + */ + private static buildMap(): void { + if (Object.keys(Base58.ALPHABET_MAP).length == 0) { + let i = 0; + while (i < Base58.ALPHABET.length) { + Base58.ALPHABET_MAP[Base58.ALPHABET.charAt(i)] = i; + i++; + } + } + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/ed25519.ts b/packages/vm/wasmlib/ts/wasmlib/client/crypto/ed25519.ts new file mode 100644 index 0000000000..c01b033571 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/crypto/ed25519.ts @@ -0,0 +1,36 @@ +import nacl from 'tweetnacl'; +import { Buffer } from '../buffer'; +import type { IKeyPair } from '../models'; + +/** + * Class to help with ED25519 Signature scheme. + */ +export class ED25519 { + public static VERSION: number = 0; + public static PUBLIC_KEY_SIZE: number = 32; + public static SIGNATURE_SIZE: number = 64; + + /** + * Generate a key pair from the seed. + * @param seed The seed to generate the key pair from. + * @returns The key pair. + */ + public static keyPairFromSeed(seed: Buffer): IKeyPair { + const signKeyPair = nacl.sign.keyPair.fromSeed(seed); + + return { + publicKey: Buffer.from(signKeyPair.publicKey), + secretKey: Buffer.from(signKeyPair.secretKey), + }; + } + + /** + * Privately sign the data. + * @param keyPair The key pair to sign with. + * @param buffer The data to sign. + * @returns The signature. + */ + public static privateSign(keyPair: IKeyPair, buffer: Buffer): Buffer { + return Buffer.from(nacl.sign.detached(buffer, keyPair.secretKey)); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/hname.ts b/packages/vm/wasmlib/ts/wasmlib/client/crypto/hname.ts new file mode 100644 index 0000000000..dee4a6d40e --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/crypto/hname.ts @@ -0,0 +1,25 @@ +import { blake2b } from 'blakejs'; +import { Buffer } from '../buffer'; + +export class HName { + public static HashAsString(textToHash: string): string { + const hashNumber = this.HashAsNumber(textToHash); + const hashString = hashNumber.toString(16); + + return hashString; + } + + public static HashAsBuffer(textToHash: string): Buffer { + const buffer = Buffer.from(textToHash); + const result = blake2b(buffer, undefined, 32); + + return Buffer.from(result); + } + + public static HashAsNumber(textToHash: string): number { + const bufferedHash = this.HashAsBuffer(textToHash); + const resultHash = Buffer.from(bufferedHash).readUInt32LE(0); + + return resultHash; + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/index.ts b/packages/vm/wasmlib/ts/wasmlib/client/crypto/index.ts new file mode 100644 index 0000000000..21203dfba2 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/crypto/index.ts @@ -0,0 +1,6 @@ +// @index('./*.ts', f => `export * from '${f.path}'`) +export * from './base58'; +export * from './ed25519'; +export * from './hname'; +export * from './seed'; +// @endindex diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/seed.ts b/packages/vm/wasmlib/ts/wasmlib/client/crypto/seed.ts new file mode 100644 index 0000000000..cc805894a5 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/crypto/seed.ts @@ -0,0 +1,97 @@ +import { Base58 } from './base58'; +import { blake2b } from 'blakejs'; +import { Buffer } from '../buffer'; +import { ED25519 } from './ed25519'; +import type { IKeyPair } from '../models'; + +export class Seed { + /** + * SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. + */ + public static SEED_SIZE: number = 32; + /** + * Generate a new seed. + * @returns The random seed. + */ + public static generate(): Buffer { + const cryptoObj: Crypto = window.crypto; + const array = new Uint32Array(Seed.SEED_SIZE); + + cryptoObj.getRandomValues(array); + + return Buffer.from(array); + } + + /** + * Generate the nth subseed. + * @param seed The seed to generate from. + * @param index The index of the subseed to generate. + * @returns The subseed. + */ + public static subseed(seed: Buffer, index: number): Buffer { + const indexBytes = Buffer.alloc(8); + indexBytes.writeBigUInt64LE(BigInt(index), 0); + + const hashOfPaddedBuffer = blake2b(indexBytes, undefined, 32 /* Blake256 */); + + const subseed = Buffer.alloc(this.SEED_SIZE); + Seed.xorBytes(seed, Buffer.from(hashOfPaddedBuffer), subseed); + return subseed; + } + + /** + * Is the string a valid seed. + * @param seed The seed to check. + * @returns True is the seed is valid. + */ + public static isValid(seed?: string): boolean { + if (!seed) { + return false; + } + if (!Base58.isValid(seed)) { + return false; + } + return Base58.decode(seed).length == Seed.SEED_SIZE; + } + + /** + * Generate a key pair for the seed index. + * @param seed The seed. + * @param index The index of the address to generate. + * @returns The generated address key pair. + */ + public static generateKeyPair(seed: Buffer, index: number): IKeyPair { + const subSeed = Seed.subseed(seed, index); + return ED25519.keyPairFromSeed(subSeed); + } + + /** + * Generate an address for the seed. + * @param seed The seed. + * @param index The index of the address to generate. + * @returns The generated address. + */ + public static generateAddress(seed: Buffer, index: number): string { + const { publicKey } = Seed.generateKeyPair(seed, index); + + const digest = blake2b(publicKey, undefined, 32); + + const buffer = Buffer.alloc(Seed.SEED_SIZE + 1); + buffer[0] = ED25519.VERSION; + Buffer.from(digest).copy(buffer, 1); + + return Base58.encode(buffer); + } + + /** + * XOR the two input buffers into the output. + * @param srcA The first source buffer. + * @param srcB The second source buffer, + * @param dest The destination buffer. + */ + private static xorBytes(srcA: Buffer, srcB: Buffer, dest: Buffer): void { + for (let i = 0; i < srcA.byteLength; i++) { + dest.writeUInt8(srcA.readUInt8(i) ^ srcB.readUInt8(i), i); + } + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/event.ts b/packages/vm/wasmlib/ts/wasmlib/client/event.ts new file mode 100644 index 0000000000..747cd96148 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/event.ts @@ -0,0 +1,93 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as client from "./index" +import {Base58} from "./crypto"; + +export class Event { + private index: client.Int32; + private message: string[]; + public readonly timestamp: client.Int32; + + constructor(message: string[]) { + this.message = message; + this.index = 0; + this.timestamp = Number(this.next()); + } + + private next(): string { + return this.message[this.index++]; + } + + nextAddress(): client.Address { + return this.next(); + } + + nextAgentID(): client.AgentID { + return this.next(); + } + + nextBool(): client.Bool { + return this.next() != "0"; + } + + nextBytes(): client.Bytes { + return Base58.decode(this.next()); + } + + nextChainID(): client.ChainID { + return this.next(); + } + + nextColor(): client.Color { + return this.next(); + } + + nextHash(): client.Hash { + return this.next(); + } + + nextHname(): client.Hname { + return Number(this.next()); + } + + nextInt8(): client.Int8 { + return Number(this.next()); + } + + nextInt16(): client.Int16 { + return Number(this.next()); + } + + nextInt32(): client.Int32 { + return Number(this.next()); + } + + nextInt64(): client.Int64 { + return BigInt(this.next()); + } + + nextRequestID(): client.RequestID { + return this.next(); + } + + nextString(): string { + return this.next(); + } + + nextUint8(): client.Uint8 { + return Number(this.next()); + } + + nextUint16(): client.Uint16 { + return Number(this.next()); + } + + nextUint32(): client.Uint32 { + return Number(this.next()); + } + + nextUint64(): client.Uint64 { + return BigInt(this.next()); + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/index.ts b/packages/vm/wasmlib/ts/wasmlib/client/index.ts new file mode 100644 index 0000000000..ca4768dd66 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/index.ts @@ -0,0 +1,8 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from "./arguments"; +export * from "./event"; +export * from "./results"; +export * from "./service"; +export * from "./types"; diff --git a/packages/vm/wasmlib/ts/wasmlib/client/results.ts b/packages/vm/wasmlib/ts/wasmlib/client/results.ts new file mode 100644 index 0000000000..cded7b816f --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/results.ts @@ -0,0 +1,105 @@ +// The Results struct is used to gather all arguments for a smart +// contract function call and encode it into a deterministic byte array +import * as wasmlib from "../index"; +import * as client from "./index"; +import {Base58} from "./crypto"; +import {Buffer} from "./buffer"; + +export class Results { + res = new Map(); + + exists(key: string): client.Bool { + return this.res.has(key); + } + + private get(key: string, typeID: client.Int32): client.Bytes { + let size = wasmlib.TYPE_SIZES[typeID]; + let bytes = this.res.get(key); + if (bytes !== undefined) { + if (size != 0 && bytes.length != size) { + client.panic("invalid type size"); + } + return bytes; + } + // return default all-zero bytes value + return Buffer.alloc(size); + } + + private getBase58(key: string, typeID: client.Int32): string { + return Base58.encode(this.get(key, typeID)); + } + + getAddress(key: string): client.Address { + return this.getBase58(key, wasmlib.TYPE_ADDRESS); + } + + getAgentID(key: string): client.AgentID { + return this.getBase58(key, wasmlib.TYPE_AGENT_ID); + } + + getBytes(key: string): client.Bytes { + return this.get(key, wasmlib.TYPE_BYTES) + } + + getBool(key: string): client.Bool { + return this.get(key, wasmlib.TYPE_BOOL)[0] != 0; + } + + getChainID(key: string): client.ChainID { + return this.getBase58(key, wasmlib.TYPE_CHAIN_ID); + } + + getColor(key: string): client.Color { + return this.getBase58(key, wasmlib.TYPE_COLOR); + } + + getHash(key: string): client.Hash { + return this.getBase58(key, wasmlib.TYPE_HASH); + } + + getHname(key: string): client.Hname { + return this.get(key, wasmlib.TYPE_HNAME).readUInt32LE(0); + } + + getInt8(key: string): client.Int8 { + return this.get(key, wasmlib.TYPE_INT8).readInt8(0); + } + + getInt16(key: string): client.Int16 { + return this.get(key, wasmlib.TYPE_INT16).readInt16LE(0); + } + + getInt32(key: string): client.Int32 { + return this.get(key, wasmlib.TYPE_INT32).readInt32LE(0); + } + + getInt64(key: string): client.Int64 { + return this.get(key, wasmlib.TYPE_INT64).readBigInt64LE(0); + } + + getRequestID(key: string): client.RequestID { + return this.getBase58(key, wasmlib.TYPE_REQUEST_ID); + } + + getString(key: string): client.String { + return this.get(key, wasmlib.TYPE_STRING).toString(); + } + + getUint8(key: string): client.Uint8 { + return this.get(key, wasmlib.TYPE_INT8).readUInt8(0); + } + + getUint16(key: string): client.Uint16 { + return this.get(key, wasmlib.TYPE_INT16).readUInt16LE(0); + } + + getUint32(key: string): client.Uint32 { + return this.get(key, wasmlib.TYPE_INT32).readUInt32LE(0); + } + + getUint64(key: string): client.Uint64 { + return this.get(key, wasmlib.TYPE_INT64).readBigUInt64LE(0); + } + + // TODO Decode() from view call response into map +} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/service.ts b/packages/vm/wasmlib/ts/wasmlib/client/service.ts new file mode 100644 index 0000000000..9bc87cdcb4 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/service.ts @@ -0,0 +1,90 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasp from "../../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; +import {Colors, IOffLedger, OffLedger} from "../../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; +import config from './config.dev'; +import * as client from "./index" + +export type ServiceClient = wasp.BasicClient; + +export type EventHandlers = { [key: string]: (message: string[]) => void }; + +export class Service { + private client: ServiceClient; + private walletService: wasp.WalletService; + private webSocket: WebSocket; + private eventHandlers: EventHandlers; + public chainId: string; + public scHname: string; + + constructor(client: ServiceClient, chainId: string, scHname: string, eventHandlers: EventHandlers) { + this.client = client; + this.chainId = chainId; + this.scHname = scHname; + this.eventHandlers = eventHandlers; + this.walletService = new wasp.WalletService(client); + this.connectWebSocket(); + } + + private connectWebSocket(): void { + const webSocketUrl = config.waspWebSocketUrl.replace('%chainId', this.chainId); + // eslint-disable-next-line no-console + console.log(`Connecting to Websocket => ${webSocketUrl}`); + this.webSocket = new WebSocket(webSocketUrl); + this.webSocket.addEventListener('message', (x) => this.handleIncomingMessage(x)); + this.webSocket.addEventListener('close', () => setTimeout(this.connectWebSocket.bind(this), 1000)); + } + + private handleIncomingMessage(message: MessageEvent): void { + const msg = message.data.toString().split('|'); + if (msg.length == 0) { + return; + } + const topics = msg[0].split(' '); + if (msg[0] != 'vmmsg') { + return; + } + const topic = msg[3]; + if (typeof this.eventHandlers[topic] != 'undefined') { + this.eventHandlers[topic](msg.slice(1)); + } + } + + // calls a view + public async callView(viewName: string, args: client.Arguments): Promise { + const response = await this.client.callView( + this.chainId, + this.scHname.toString(), + viewName, + args, + ); + + const resultMap: ParameterResult = {}; + if (response.Items) { + for (let item of response.Items) { + const key = wasp.Buffer.from(item.Key, "base64").toString(); + const value = wasp.Buffer.from(item.Value, "base64"); + resultMap[key] = value; + } + } + return resultMap; + } + + // posts off-tangle request + public async postRequest(hFunc: client.Hname, args: client.Arguments): Promise { + let request: IOffLedger = { + requestType: 1, + noonce: BigInt(performance.now() + performance.timeOrigin * 10000000), + contract: this.scHname, + entrypoint: hFunc, + arguments: [{key: '-number', value: betNumber}], + balances: [{balance: take, color: Colors.IOTA_COLOR_BYTES}], + }; + + request = OffLedger.Sign(request, keyPair); + + await this.client.sendOffLedgerRequest(this.chainId, request); + await this.client.sendExecutionRequest(this.chainId, OffLedger.GetRequestId(request)); + } +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json b/packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json new file mode 100644 index 0000000000..80d11b5866 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": "es2020", + "target": "es2020", + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/client/types.ts b/packages/vm/wasmlib/ts/wasmlib/client/types.ts new file mode 100644 index 0000000000..821b200ace --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/client/types.ts @@ -0,0 +1,28 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import {Buffer} from "./buffer"; + +export type Address = string; +export type AgentID = string; +export type Bool = boolean; +export type Bytes = Buffer; +export type ChainID = string; +export type Color = string; +export type Hash = string; +export type Hname = number; +export type Int8 = number; +export type Int16 = number; +export type Int32 = number; +export type Int64 = bigint; +export type RequestID = string; +export type String = string; +export type Uint8 = number; +export type Uint16 = number; +export type Uint32 = number; +export type Uint64 = bigint; + + +export function panic(err: string) { + throw new Error(err); +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmlib/context.ts b/packages/vm/wasmlib/ts/wasmlib/context.ts index 13ab878698..72bcd296b6 100644 --- a/packages/vm/wasmlib/ts/wasmlib/context.ts +++ b/packages/vm/wasmlib/ts/wasmlib/context.ts @@ -319,6 +319,9 @@ export class ScFuncContext extends ScBaseContext implements ScViewCallContext, S // generates a random value from 0 to max (exclusive max) using a deterministic RNG random(max: i64): i64 { + if (max == 0) { + this.panic("random: max parameter should be non-zero"); + } let state = new ScMutableMap(OBJ_ID_STATE); let rnd = state.getBytes(keys.KEY_RANDOM); let seed = rnd.value(); diff --git a/packages/vm/wasmlib/ts/wasmlib/host.ts b/packages/vm/wasmlib/ts/wasmlib/host.ts index a830b80eb0..4337d407fc 100644 --- a/packages/vm/wasmlib/ts/wasmlib/host.ts +++ b/packages/vm/wasmlib/ts/wasmlib/host.ts @@ -37,7 +37,7 @@ export const OBJ_ID_PARAMS: i32 = 3; export const OBJ_ID_RESULTS: i32 = 4; // size in bytes of predefined types, indexed by the TYPE_* consts -const TYPE_SIZES: u8[] = [0, 33, 37, 1, 0, 33, 32, 32, 4, 1, 2, 4, 8, 0, 34, 0]; +export const TYPE_SIZES: u8[] = [0, 33, 37, 1, 0, 33, 32, 32, 4, 1, 2, 4, 8, 0, 34, 0]; // These 4 external functions are funneling the entire WasmLib functionality diff --git a/tools/schema/generator/generator_client.go b/tools/schema/generator/clientbase.go similarity index 55% rename from tools/schema/generator/generator_client.go rename to tools/schema/generator/clientbase.go index bc48b6369f..26745af5e6 100644 --- a/tools/schema/generator/generator_client.go +++ b/tools/schema/generator/clientbase.go @@ -1,27 +1,17 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - package generator import ( "fmt" "os" - "github.com/iotaledger/wasp/tools/schema/generator/clienttemplates" "github.com/iotaledger/wasp/tools/schema/model" ) -type ClientGenerator struct { +type ClientBase struct { GenBase } -func NewClientGenerator(s *model.Schema) *ClientGenerator { - g := &ClientGenerator{} - g.init(s, clienttemplates.TypeDependent, clienttemplates.Templates) - return g -} - -func (g *ClientGenerator) Generate() error { +func (g *ClientBase) Generate() error { g.folder = g.rootFolder + "/" err := os.MkdirAll(g.folder, 0o755) if err != nil { @@ -37,18 +27,25 @@ func (g *ClientGenerator) Generate() error { return g.generateCode() } -func (g *ClientGenerator) generateCode() error { +func (g *ClientBase) generateCode() error { err := g.createSourceFile("events", len(g.s.Events) != 0) if err != nil { return err } - err = g.createSourceFile("app", len(g.s.Events) != 0) - if err != nil { - return err - } err = g.createSourceFile("service", len(g.s.Events) != 0) if err != nil { return err } - return nil + return g.generateFuncs(g.appendEvents) +} + +func (g *ClientBase) appendEvents(existing model.StringMap) { + for _, g.currentEvent = range g.s.Events { + name := g.s.ContractName + capitalize(g.currentEvent.Name) + if existing[name] == "" { + g.log("currentEvent: " + g.currentEvent.Name) + g.setMultiKeyValues("evtName", g.currentEvent.Name) + g.emit("funcSignature") + } + } } diff --git a/tools/schema/generator/clienttemplates/alltemplates.go b/tools/schema/generator/clienttemplates/alltemplates.go deleted file mode 100644 index 2fca82b07b..0000000000 --- a/tools/schema/generator/clienttemplates/alltemplates.go +++ /dev/null @@ -1,87 +0,0 @@ -package clienttemplates - -import "github.com/iotaledger/wasp/tools/schema/model" - -var config = map[string]string{ - "language": "Client", - "extension": ".ts", - "rootFolder": "client", - "funcRegexp": `N/A`, -} - -var Templates = []map[string]string{ - config, - common, - appTs, - eventsTs, - serviceTs, -} - -var TypeDependent = model.StringMapMap{ - "msgConvert": { - "Address": "message[++index]", - "AgentID": "message[++index]", - "Bool": "message[++index][0]!='0'", - "ChainID": "message[++index]", - "Color": "message[++index]", - "Hash": "message[++index]", - "Hname": "message[++index]", - "Int8": "Number(message[++index])", - "Int16": "Number(message[++index])", - "Int32": "Number(message[++index])", - "Int64": "BigInt(message[++index])", - "RequestID": "message[++index]", - "String": "message[++index]", - "Uint8": "Number(message[++index])", - "Uint16": "Number(message[++index])", - "Uint32": "Number(message[++index])", - "Uint64": "BigInt(message[++index])", - }, - "fldDefault": { - "Address": "''", - "AgentID": "''", - "Bool": "false", - "ChainID": "''", - "Color": "''", - "Hash": "''", - "Hname": "''", - "Int8": "0", - "Int16": "0", - "Int32": "0", - "Int64": "BigInt(0)", - "RequestID": "''", - "String": "''", - "Uint8": "0", - "Uint16": "0", - "Uint32": "0", - "Uint64": "BigInt(0)", - }, - "resConvert": { - "Address": "toString", - "AgentID": "toString", - "Bool": "readUInt8", - "ChainID": "toString", - "Color": "toString", - "Hash": "toString", - "Hname": "toString", - "Int8": "readInt8", - "Int16": "readInt16LE", - "Int32": "readInt32LE", - "Int64": "readBigInt64LE", - "RequestID": "toString", - "String": "toString", - "Uint8": "readUInt8", - "Uint16": "readUInt16LE", - "Uint32": "readUInt32LE", - "Uint64": "readBigUInt64LE", - }, - "resConvert2": { - "Bool": "!=0", - }, -} - -var common = map[string]string{ - // ******************************* - "tmp": ` -tmp`, -} diff --git a/tools/schema/generator/clienttemplates/app.go b/tools/schema/generator/clienttemplates/app.go deleted file mode 100644 index cee2726e7a..0000000000 --- a/tools/schema/generator/clienttemplates/app.go +++ /dev/null @@ -1,22 +0,0 @@ -package clienttemplates - -var appTs = map[string]string{ - // ******************************* - "app.ts": ` -import * as wasmlib from "./wasmlib" -import * as events from "./events" -import * as service from "./service" - -let $pkgName$+Service: service.$PkgName$+Service; - -export function subscribeTo$Package$+Events(): void { -$#each events appOnEvent -} -`, - // ******************************* - "appOnEvent": ` - - $pkgName$+Service.on('$package$+_$evtName', (event: events.Event$EvtName) => { - }); -`, -} diff --git a/tools/schema/generator/clienttemplates/events.go b/tools/schema/generator/clienttemplates/events.go deleted file mode 100644 index ca14b503d1..0000000000 --- a/tools/schema/generator/clienttemplates/events.go +++ /dev/null @@ -1,58 +0,0 @@ -package clienttemplates - -var eventsTs = map[string]string{ - // ******************************* - "events.ts": ` -import * as wasmlib from "./wasmlib" -import * as service from "./service" - -$#each events eventInterface - -export interface $Package$+Events { -$#each events eventSignature -} - -export function handleVmMessage(message: string[]): void { - const messageHandlers: wasmlib.MessageHandlers = { -$#each events eventHandler - }; - - const topicIndex = 3; - const topic = message[topicIndex]; - - if (typeof messageHandlers[topic] != 'undefined') { - messageHandlers[topic](topicIndex); - } -} -`, - // ******************************* - "eventSignature": ` - $package$+_$evtName: (event: Event$EvtName) => void; -`, - // ******************************* - "eventInterface": ` - -export interface Event$EvtName { - timestamp: wasmlib.Int32; -$#each event eventInterfaceField -} -`, - // ******************************* - "eventInterfaceField": ` - $fldName: wasmlib.$FldType; -`, - // ******************************* - "eventHandler": ` - '$package.$evtName': (index) => { - const evt: Event$EvtName = { - timestamp: Number(message[++index]), -$#each event eventHandlerField - }; - this.emitter.emit('$package$+_$evtName', evt); - }, -`, - // ******************************* - "eventHandlerField": ` - $fldName: $msgConvert, -`, -} diff --git a/tools/schema/generator/clienttemplates/service.go b/tools/schema/generator/clienttemplates/service.go deleted file mode 100644 index f67dc8de6d..0000000000 --- a/tools/schema/generator/clienttemplates/service.go +++ /dev/null @@ -1,82 +0,0 @@ -package clienttemplates - -var serviceTs = map[string]string{ - // ******************************* - "service.ts": ` -import * as wasmlib from "./wasmlib" -import * as events from "./events" -$#each func serviceResults - -export class $PkgName$+Service extends wasmlib.Service { - - constructor(client: BasicClient, chainId: string) { - super(client, chainId, 0x$hscName); - } -$#each func serviceFunction -} -`, - // ******************************* - "serviceResults": ` -$#if result serviceHasResults -`, - // ******************************* - "serviceHasResults": ` - -export interface $FuncName$+Result { -$#each result serviceResult -} -`, - // ******************************* - "serviceResult": ` - $fldName: wasmlib.$FldType; -`, - // ******************************* - "serviceFunction": ` -$#set sep $empty -$#set params $empty -$#each param serviceParam -$#emit service$Kind -`, - // ******************************* - "serviceParam": ` -$#set params $params$sep$fldName: wasmlib.$FldType -$#set sep , -`, - // ******************************* - "serviceFunc": ` - - public async $funcName($params): Promise { - const args: wasmlib.Argument[] = [ -$#each param serviceFuncParam - ]; - await this.postRequest(0x$funcHname, args); - } -`, - // ******************************* - "serviceFuncParam": ` - { key: '$fldName', value: $fldName, }, -`, - // ******************************* - "serviceView": ` - - public async $funcName($params): Promise<$FuncName$+Result> { - const args: wasmlib.Argument[] = [ -$#each param serviceFuncParam - ]; - const response = await this.callView(0x$funcHname, args); - let result: $FuncName$+Result = {}; - -$#each result serviceViewResult - - return result; - } -`, - // ******************************* - "serviceViewResult": ` - let $fldName = response['$fldName']; - result.$fldName = $fldDefault; - if ($fldName) { - result.$fldName = $fldName.$resConvert($fldName)$resConvert2; - } -`, -} diff --git a/tools/schema/generator/emitter.go b/tools/schema/generator/emitter.go index 7f965c837c..efca197f31 100644 --- a/tools/schema/generator/emitter.go +++ b/tools/schema/generator/emitter.go @@ -279,6 +279,8 @@ func (g *GenBase) emitIf(line string) { condition = g.keys["kind"] == KeyFunc case KeyInit: condition = g.currentFunc.Name == KeyInit + case KeyMandatory: + condition = !g.currentField.Optional case KeyMap: condition = g.currentField.MapKey != "" case KeyMut: diff --git a/tools/schema/generator/generator.go b/tools/schema/generator/generator.go index 221813c841..106ed72eb9 100644 --- a/tools/schema/generator/generator.go +++ b/tools/schema/generator/generator.go @@ -190,12 +190,12 @@ func (g *GenBase) generateCode() error { return err } if !g.s.CoreContracts { - return g.generateFuncs() + return g.generateFuncs(g.appendFuncs) } return nil } -func (g *GenBase) generateFuncs() error { +func (g *GenBase) generateFuncs(appendFuncs func(existing model.StringMap)) error { scFileName := g.folder + g.s.PackageName + g.extension if g.exists(scFileName) != nil { // generate initial SC function file @@ -229,12 +229,7 @@ func (g *GenBase) generateFuncs() error { } // append any new funcs - for _, g.currentFunc = range g.s.Funcs { - if existing[g.funcName(g.currentFunc)] == "" { - g.setFuncKeys(false, 0, 0) - g.emit("funcSignature") - } - } + appendFuncs(existing) }) if err != nil { return err @@ -242,6 +237,15 @@ func (g *GenBase) generateFuncs() error { return os.Remove(scOriginal) } +func (g *GenBase) appendFuncs(existing model.StringMap) { + for _, g.currentFunc = range g.s.Funcs { + if existing[g.funcName(g.currentFunc)] == "" { + g.setFuncKeys(false, 0, 0) + g.emit("funcSignature") + } + } +} + func (g *GenBase) generateTests() error { err := os.MkdirAll("test", 0o755) if err != nil { diff --git a/tools/schema/generator/generator_goclient.go b/tools/schema/generator/generator_goclient.go new file mode 100644 index 0000000000..319b55c529 --- /dev/null +++ b/tools/schema/generator/generator_goclient.go @@ -0,0 +1,19 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "github.com/iotaledger/wasp/tools/schema/generator/goclienttemplates" + "github.com/iotaledger/wasp/tools/schema/model" +) + +type GoClientGenerator struct { + ClientBase +} + +func NewGoClientGenerator(s *model.Schema) *GoClientGenerator { + g := &GoClientGenerator{} + g.init(s, goclienttemplates.TypeDependent, goclienttemplates.Templates) + return g +} diff --git a/tools/schema/generator/generator_tsclient.go b/tools/schema/generator/generator_tsclient.go new file mode 100644 index 0000000000..f4b475deab --- /dev/null +++ b/tools/schema/generator/generator_tsclient.go @@ -0,0 +1,19 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "github.com/iotaledger/wasp/tools/schema/generator/tsclienttemplates" + "github.com/iotaledger/wasp/tools/schema/model" +) + +type TsClientGenerator struct { + ClientBase +} + +func NewTsClientGenerator(s *model.Schema) *TsClientGenerator { + g := &TsClientGenerator{} + g.init(s, tsclienttemplates.TypeDependent, tsclienttemplates.Templates) + return g +} diff --git a/tools/schema/generator/goclienttemplates/alltemplates.go b/tools/schema/generator/goclienttemplates/alltemplates.go new file mode 100644 index 0000000000..6861075815 --- /dev/null +++ b/tools/schema/generator/goclienttemplates/alltemplates.go @@ -0,0 +1,165 @@ +package goclienttemplates + +import "github.com/iotaledger/wasp/tools/schema/model" + +var config = map[string]string{ + "language": "Go Client", + "extension": ".go", + "rootFolder": "client/go", + "funcRegexp": `^func On(\w+).+$`, +} + +var Templates = []map[string]string{ + config, + common, + funcsGo, + eventsGo, + serviceGo, +} + +var TypeDependent = model.StringMapMap{ + "fldLangType": { + "Address": "client.Address", + "AgentID": "client.AgentID", + "Bool": "bool", + "Bytes": "[]byte", + "ChainID": "client.ChainID", + "Color": "client.Color", + "Hash": "client.Hash", + "Hname": "string", + "Int8": "int8", + "Int16": "int16", + "Int32": "int32", + "Int64": "int64", + "RequestID": "client.RequestID", + "String": "string", + "Uint8": "uint8", + "Uint16": "uint16", + "Uint32": "uint32", + "Uint64": "uint64", + }, + "fldTypeID": { + "Address": "wasmlib.TYPE_ADDRESS", + "AgentID": "wasmlib.TYPE_AGENT_ID", + "Bool": "wasmlib.TYPE_BOOL", + "Bytes": "wasmlib.TYPE_BYTES", + "ChainID": "wasmlib.TYPE_CHAIN_ID", + "Color": "wasmlib.TYPE_COLOR", + "Hash": "wasmlib.TYPE_HASH", + "Hname": "wasmlib.TYPE_HNAME", + "Int8": "wasmlib.TYPE_INT8", + "Int16": "wasmlib.TYPE_INT16", + "Int32": "wasmlib.TYPE_INT32", + "Int64": "wasmlib.TYPE_INT64", + "RequestID": "wasmlib.TYPE_REQUEST_ID", + "String": "wasmlib.TYPE_STRING", + "Uint8": "wasmlib.TYPE_INT8", + "Uint16": "wasmlib.TYPE_INT16", + "Uint32": "wasmlib.TYPE_INT32", + "Uint64": "wasmlib.TYPE_INT64", + "": "wasmlib.TYPE_BYTES", + }, + "argEncode": { + "Address": "client.Base58Decode", + "AgentID": "client.Base58Decode", + "Bool": "codec.EncodeBool", + "Bytes": "[]byte", + "ChainID": "client.Base58Decode", + "Color": "client.Base58Decode", + "Hash": "client.Base58Decode", + "Hname": "client.Base58Decode", + "Int8": "codec.EncodeInt8", + "Int16": "codec.EncodeInt16", + "Int32": "codec.EncodeInt32", + "Int64": "codec.EncodeInt64", + "RequestID": "client.Base58Decode", + "String": "codec.EncodeString", + "Uint8": "codec.EncodeUint8", + "Uint16": "codec.EncodeUint16", + "Uint32": "codec.EncodeUint32", + "Uint64": "codec.EncodeUint64", + }, + "argDecode": { + "Address": "client.Base58Encode", + "AgentID": "client.Base58Encode", + "Bool": "codec.DecodeBool", + "Bytes": "[]byte", + "ChainID": "client.Base58Encode", + "Color": "client.Base58Encode", + "Hash": "client.Base58Encode", + "Hname": "client.Base58Encode", + "Int8": "codec.DecodeInt8", + "Int16": "codec.DecodeInt16", + "Int32": "codec.DecodeInt32", + "Int64": "codec.DecodeInt64", + "RequestID": "client.Base58Encode", + "String": "codec.DecodeString", + "Uint8": "codec.DecodeUint8", + "Uint16": "codec.DecodeUint16", + "Uint32": "codec.DecodeUint32", + "Uint64": "codec.DecodeUint64", + }, + "msgConvert": { + "Address": "e.Next()", + "AgentID": "e.Next()", + "Bool": "e.NextBool()", + "ChainID": "e.Next()", + "Color": "e.Next()", + "Hash": "e.Next()", + "Hname": "e.Next()", + "Int8": "e.NextInt8()", + "Int16": "e.NextInt16()", + "Int32": "e.NextInt32()", + "Int64": "e.NextInt64()", + "RequestID": "e.Next()", + "String": "e.Next()", + "Uint8": "e.NextUint8()", + "Uint16": "e.NextUint16()", + "Uint32": "e.NextUint32()", + "Uint64": "e.NextUint64()", + }, + "fldDefault": { + "Address": "''", + "AgentID": "''", + "Bool": "false", + "ChainID": "''", + "Color": "''", + "Hash": "''", + "Hname": "''", + "Int8": "0", + "Int16": "0", + "Int32": "0", + "Int64": "BigInt(0)", + "RequestID": "''", + "String": "''", + "Uint8": "0", + "Uint16": "0", + "Uint32": "0", + "Uint64": "BigInt(0)", + }, + "resConvert": { + "Address": "toString()", + "AgentID": "toString()", + "Bool": "readUInt8(0)!=0", + "ChainID": "toString()", + "Color": "toString()", + "Hash": "toString()", + "Hname": "toString()", + "Int8": "readInt8(0)", + "Int16": "readInt16LE(0)", + "Int32": "readInt32LE(0)", + "Int64": "readBigInt64LE(0)", + "RequestID": "toString()", + "String": "toString()", + "Uint8": "readUInt8(0)", + "Uint16": "readUInt16LE(0)", + "Uint32": "readUInt32LE(0)", + "Uint64": "readBigUInt64LE(0)", + }, +} + +var common = map[string]string{ + // ******************************* + "tmp": ` +tmp`, +} diff --git a/tools/schema/generator/goclienttemplates/events.go b/tools/schema/generator/goclienttemplates/events.go new file mode 100644 index 0000000000..11707c5667 --- /dev/null +++ b/tools/schema/generator/goclienttemplates/events.go @@ -0,0 +1,44 @@ +package goclienttemplates + +var eventsGo = map[string]string{ + // ******************************* + "events.go": ` +package $package + +import ( + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" +) + +var EventHandlers = map[string]func([]string) { +$#each events eventHandler +} +$#each events eventClass +`, + // ******************************* + "eventHandler": ` + "$package.$evtName": on$PkgName$EvtName$+Thunk, +`, + // ******************************* + "eventClass": ` + +type Event$EvtName struct { + client.Event +$#each event eventClassField +} + +func on$PkgName$EvtName$+Thunk(message []string) { + e := &Event$EvtName{} + e.Init(message) +$#each event eventHandlerField + On$PkgName$EvtName(e) +} +`, + // ******************************* + "eventClassField": ` + $FldName $fldLangType +`, + // ******************************* + "eventHandlerField": ` + e.$FldName = e.Next$FldType() +`, +} diff --git a/tools/schema/generator/goclienttemplates/funcs.go b/tools/schema/generator/goclienttemplates/funcs.go new file mode 100644 index 0000000000..dd3dfd6af9 --- /dev/null +++ b/tools/schema/generator/goclienttemplates/funcs.go @@ -0,0 +1,19 @@ +package goclienttemplates + +var funcsGo = map[string]string{ + // ******************************* + "funcs.go": ` +package $package + +import ( + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" +) +$#each events funcSignature +`, + // ******************************* + "funcSignature": ` + +func On$PkgName$EvtName(event *Event$EvtName) { +} +`, +} diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go new file mode 100644 index 0000000000..500797ec64 --- /dev/null +++ b/tools/schema/generator/goclienttemplates/service.go @@ -0,0 +1,132 @@ +package goclienttemplates + +var serviceGo = map[string]string{ + // ******************************* + "service.go": ` +package $package + +import ( + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" +) + +const ( +$#each params constArg + +$#each results constRes +) +$#each func funcStruct + +type $PkgName$+Service struct { + client.Service +} + +func New$PkgName$+Service(client client.ServiceClient, chainId string) *$PkgName$+Service { + s := &$PkgName$+Service{} + s.Service.Init(client, chainId, "$hscName", EventHandlers) + return s +} +$#each func serviceFunction +`, + // ******************************* + "constArg": ` + Arg$FldName = "$fldAlias" +`, + // ******************************* + "constRes": ` + Res$FldName = "$fldAlias" +`, + // ******************************* + "funcStruct": ` + +type $FuncName$Kind struct { +$#if param funcArgsMember +} +$#each param funcArgSetter +$#if func funcPost viewCall +`, + // ******************************* + "funcArgsMember": ` + args client.Arguments +`, + // ******************************* + "funcArgSetter": ` + +func (f $FuncName$Kind) $FldName(v $fldLangType) { + f.args.Set$FldType(Arg$FldName, v) +} +`, + // ******************************* + "funcPost": ` + +func (f $FuncName$Kind) Post() { +$#each param mandatoryCheck +$#set exec Post +$#if param execWithArgs execNoArgs + //TODO Do$exec +} +`, + // ******************************* + "viewCall": ` + +func (f $FuncName$Kind) Call() $FuncName$+Results { +$#each param mandatoryCheck +$#set exec Call +$#if param execWithArgs execNoArgs + //TODO Do$exec instead of client.NewResults() + return $FuncName$+Results { res: client.NewResults() } +} +$#if result resultStruct +`, + // ******************************* + "mandatoryCheck": ` + f.args.Mandatory(Arg$FldName) +`, + // ******************************* + "execWithArgs": ` +$#set exec $exec(f.args) +`, + // ******************************* + "execNoArgs": ` +$#set exec $exec(nil) +`, + // ******************************* + "resultStruct": ` + +type $FuncName$+Results struct { + res client.Results +} +$#each result callResultGetter +`, + // ******************************* + "callResultGetter": ` +$#if mandatory else callResultOptional + +func (r $FuncName$+Results) $FldName() $fldLangType { + return r.res.Get$FldType(Res$FldName) +} +`, + // ******************************* + "callResultOptional": ` + +func (r $FuncName$+Results) $FldName$+Exists() bool { + return r.res.Exists(Res$FldName) +} +`, + // ******************************* + "serviceResultExtract": ` + if buf, ok := result["$fldName"]; ok { + r.$FldName = buf.$resConvert + } +`, + // ******************************* + "serviceResult": ` + $FldName $fldLangType +`, + // ******************************* + "serviceFunction": ` + +func (s *$PkgName$+Service) $FuncName() $FuncName$Kind { + return $FuncName$Kind{} +} +`, +} diff --git a/tools/schema/generator/tsclienttemplates/alltemplates.go b/tools/schema/generator/tsclienttemplates/alltemplates.go new file mode 100644 index 0000000000..821fe36d95 --- /dev/null +++ b/tools/schema/generator/tsclienttemplates/alltemplates.go @@ -0,0 +1,85 @@ +package tsclienttemplates + +import "github.com/iotaledger/wasp/tools/schema/model" + +var config = map[string]string{ + "language": "TypeScript Client", + "extension": ".ts", + "rootFolder": "client/ts", + "funcRegexp": `^export function on(\w+).+$`, +} + +var Templates = []map[string]string{ + config, + common, + funcsTs, + eventsTs, + serviceTs, +} + +var TypeDependent = model.StringMapMap{ + "fldLangType": { + "Address": "client.Address", + "AgentID": "client.AgentID", + "Bool": "boolean", + "Bytes": "client.Bytes", + "ChainID": "client.ChainID", + "Color": "client.Color", + "Hash": "client.Hash", + "Hname": "client.Hname", + "Int8": "client.Int8", + "Int16": "client.Int16", + "Int32": "client.Int32", + "Int64": "client.Int64", + "RequestID": "client.RequestID", + "String": "string", + "Uint8": "client.Uint8", + "Uint16": "client.Uint16", + "Uint32": "client.Uint32", + "Uint64": "client.Uint64", + }, + "fldDefault": { + "Address": "''", + "AgentID": "''", + "Bool": "false", + "ChainID": "''", + "Color": "''", + "Hash": "''", + "Hname": "''", + "Int8": "0", + "Int16": "0", + "Int32": "0", + "Int64": "BigInt(0)", + "RequestID": "''", + "String": "''", + "Uint8": "0", + "Uint16": "0", + "Uint32": "0", + "Uint64": "BigInt(0)", + }, + "resConvert": { + "Address": "toString()", + "AgentID": "toString()", + "Bool": "readUInt8(0)!=0", + "ChainID": "toString()", + "Color": "toString()", + "Hash": "toString()", + "Hname": "toString()", + "Int8": "readInt8(0)", + "Int16": "readInt16LE(0)", + "Int32": "readInt32LE(0)", + "Int64": "readBigInt64LE(0)", + "RequestID": "toString()", + "String": "toString()", + "Uint8": "readUInt8(0)", + "Uint16": "readUInt16LE(0)", + "Uint32": "readUInt32LE(0)", + "Uint64": "readBigUInt64LE(0)", + }, +} + +var common = map[string]string{ + // ******************************* + "tmp": ` +tmp`, +} diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go new file mode 100644 index 0000000000..84e044f0f7 --- /dev/null +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -0,0 +1,39 @@ +package tsclienttemplates + +var eventsTs = map[string]string{ + // ******************************* + "events.ts": ` +import * as client from "wasmlib/client" +import * as app from "./$package" + +export const eventHandlers: client.EventHandlers = { +$#each events eventHandler +}; +$#each events eventClass +`, + // ******************************* + "eventHandler": ` + "$package.$evtName": on$PkgName$EvtName$+Thunk, +`, + // ******************************* + "eventClass": ` + +export class Event$EvtName extends client.Event { +$#each event eventClassField +} + +function on$PkgName$EvtName$+Thunk(message: string[]) { + let e = new Event$EvtName(message); +$#each event eventHandlerField + app.on$PkgName$EvtName(e); +} +`, + // ******************************* + "eventClassField": ` + public $fldName: client.$FldType; +`, + // ******************************* + "eventHandlerField": ` + e.$fldName = e.next$FldType(); +`, +} diff --git a/tools/schema/generator/tsclienttemplates/funcs.go b/tools/schema/generator/tsclienttemplates/funcs.go new file mode 100644 index 0000000000..825912c938 --- /dev/null +++ b/tools/schema/generator/tsclienttemplates/funcs.go @@ -0,0 +1,19 @@ +package tsclienttemplates + +var funcsTs = map[string]string{ + // ******************************* + "funcs.ts": ` +import * as events from "./events" +import * as service from "./service" + +const client = new BasicClient(config); +const $pkgName$+Service = new service.$PkgName$+Service(client, config.ChainId); +$#each events funcSignature +`, + // ******************************* + "funcSignature": ` + +export function on$PkgName$EvtName(event: events.Event$EvtName): void { +} +`, +} diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go new file mode 100644 index 0000000000..b52fa0e2ce --- /dev/null +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -0,0 +1,127 @@ +package tsclienttemplates + +var serviceTs = map[string]string{ + // ******************************* + "service.ts": ` +import * as client from "wasmlib/client" +import * as events from "./events" + +$#each params constArg + +$#each results constRes +$#each func funcStruct + +export class $PkgName$+Service extends client.Service { + + constructor(client: client.ServiceClient, chainId: string) { + super(client, chainId, "$hscName", events.eventHandlers); + } +$#each func serviceFunction +} +`, + // ******************************* + "constArg": ` +const Arg$FldName = "$fldAlias"; +`, + // ******************************* + "constRes": ` +const Res$FldName = "$fldAlias"; +`, + // ******************************* + "funcStruct": ` + +export class $FuncName$Kind { +$#if param funcArgsMember +$#each param funcArgSetter +$#if func funcPost viewCall +} +$#if result resultStruct +`, + // ******************************* + "funcArgsMember": ` + args: client.Arguments = new client.Arguments(); +`, + // ******************************* + "funcArgSetter": ` + + $fldName(v: $fldLangType): void { + this.args.set$FldType(Arg$FldName, v); + } +`, + // ******************************* + "funcPost": ` + + post(): void { +$#each param mandatoryCheck +$#set exec Post +$#if param execWithArgs execNoArgs + //TODO Do$exec + } +`, + // ******************************* + "viewCall": ` + + call(): $FuncName$+Results { +$#each param mandatoryCheck +$#set exec Call +$#if param execWithArgs execNoArgs + //TODO Do$exec instead of new client.Results() + return new $FuncName$+Results(new client.Results()); + } +`, + // ******************************* + "mandatoryCheck": ` + this.args.mandatory(Arg$FldName); +`, + // ******************************* + "execWithArgs": ` +$#set exec $exec(this.args) +`, + // ******************************* + "execNoArgs": ` +$#set exec $exec(null) +`, + // ******************************* + "resultStruct": ` + +export class $FuncName$+Results { + res: client.Results; + + constructor(res: client.Results) { this.res = res; } +$#each result callResultGetter +} +`, + // ******************************* + "callResultGetter": ` +$#if mandatory else callResultOptional + + $fldName(): $fldLangType { + return this.res.get$FldType(Res$FldName); + } +`, + // ******************************* + "callResultOptional": ` + + $fldName$+Exists(): boolean { + return this.res.exists(Res$FldName) + } +`, + // ******************************* + "serviceResultExtract": ` + let $fldName = result["$fldName"]; + if ($fldName) { + this.$fldName = $fldName.$resConvert; + } +`, + // ******************************* + "serviceResult": ` + $fldName: client.$FldType; +`, + // ******************************* + "serviceFunction": ` + + public $funcName(): $FuncName$Kind { + return new $FuncName$Kind(); + } +`, +} diff --git a/tools/schema/main.go b/tools/schema/main.go index bcdb2543b6..e4662475a6 100644 --- a/tools/schema/main.go +++ b/tools/schema/main.go @@ -115,20 +115,19 @@ func generateSchema(file *os.File) error { s.SchemaTime = time.Now() } - if *flagClient { - g := generator.NewClientGenerator(s) - err = g.Generate() - if err != nil { - return err - } - } - if *flagGo { g := generator.NewGoGenerator(s) err = g.Generate() if err != nil { return err } + if *flagClient { + cg := generator.NewGoClientGenerator(s) + err = cg.Generate() + if err != nil { + return err + } + } } if *flagRust { @@ -145,6 +144,13 @@ func generateSchema(file *os.File) error { if err != nil { return err } + if *flagClient { + cg := generator.NewTsClientGenerator(s) + err = cg.Generate() + if err != nil { + return err + } + } } return nil } From d48e2e3ec45955074aaa151801170750c94c8c91 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 15 Dec 2021 16:05:38 -0800 Subject: [PATCH 010/120] Intermediate save --- client/chainclient/chainclient.go | 7 +- contracts/wasm/erc721/client/go/erc721.go | 19 + contracts/wasm/erc721/client/go/events.go | 98 ++++ contracts/wasm/erc721/client/go/service.go | 434 ++++++++++++++++++ contracts/wasm/erc721/client/ts/erc721.ts | 23 + contracts/wasm/erc721/client/ts/events.ts | 85 ++++ contracts/wasm/erc721/client/ts/index.ts | 6 + contracts/wasm/erc721/client/ts/service.ts | 405 ++++++++++++++++ contracts/wasm/erc721/client/ts/tsconfig.json | 11 + .../wasm/fairroulette/client/go/events.go | 95 ++++ .../fairroulette/client/go/fairroulette.go | 22 + .../wasm/fairroulette/client/go/service.go | 172 +++++++ .../wasm/fairroulette/client/ts/events.ts | 80 ++++ .../fairroulette/client/ts/fairroulette.ts | 26 ++ .../wasm/fairroulette/client/ts/service.ts | 179 ++++++++ .../wasm/fairroulette/client/ts/tsconfig.json | 11 + .../fair_roulette_service.ts | 38 +- .../src/lib/wasp_client/basic_client.ts | 6 +- .../vm/wasmlib/go/wasmlib/client/service.go | 8 +- .../vm/wasmlib/ts/wasmlib/client/service.ts | 40 +- .../wasmlib/ts/wasmlib/client/tsconfig.json | 2 +- .../generator/goclienttemplates/service.go | 24 +- .../generator/tsclienttemplates/service.go | 34 +- 23 files changed, 1749 insertions(+), 76 deletions(-) create mode 100644 contracts/wasm/erc721/client/go/erc721.go create mode 100644 contracts/wasm/erc721/client/go/events.go create mode 100644 contracts/wasm/erc721/client/go/service.go create mode 100644 contracts/wasm/erc721/client/ts/erc721.ts create mode 100644 contracts/wasm/erc721/client/ts/events.ts create mode 100644 contracts/wasm/erc721/client/ts/index.ts create mode 100644 contracts/wasm/erc721/client/ts/service.ts create mode 100644 contracts/wasm/erc721/client/ts/tsconfig.json create mode 100644 contracts/wasm/fairroulette/client/go/events.go create mode 100644 contracts/wasm/fairroulette/client/go/fairroulette.go create mode 100644 contracts/wasm/fairroulette/client/go/service.go create mode 100644 contracts/wasm/fairroulette/client/ts/events.ts create mode 100644 contracts/wasm/fairroulette/client/ts/fairroulette.ts create mode 100644 contracts/wasm/fairroulette/client/ts/service.ts create mode 100644 contracts/wasm/fairroulette/client/ts/tsconfig.json diff --git a/client/chainclient/chainclient.go b/client/chainclient/chainclient.go index e2d9e70c9b..28f17fdae2 100644 --- a/client/chainclient/chainclient.go +++ b/client/chainclient/chainclient.go @@ -1,6 +1,8 @@ package chainclient import ( + "time" + "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/client" @@ -21,7 +23,6 @@ type Client struct { WaspClient *client.WaspClient ChainID *iscp.ChainID KeyPair *ed25519.KeyPair - nonces map[ed25519.PublicKey]uint64 } // New creates a new chainclient.Client @@ -36,7 +37,6 @@ func New( WaspClient: waspClient, ChainID: chainID, KeyPair: keyPair, - nonces: make(map[ed25519.PublicKey]uint64), } } @@ -79,8 +79,7 @@ func (c *Client) PostOffLedgerRequest( par = params[0] } if par.Nonce == 0 { - c.nonces[c.KeyPair.PublicKey]++ - par.Nonce = c.nonces[c.KeyPair.PublicKey] + par.Nonce = uint64(time.Now().UnixNano()) } offledgerReq := request.NewOffLedger(c.ChainID, contractHname, entrypoint, par.Args).WithTransfer(par.Transfer) offledgerReq.WithNonce(par.Nonce) diff --git a/contracts/wasm/erc721/client/go/erc721.go b/contracts/wasm/erc721/client/go/erc721.go new file mode 100644 index 0000000000..e5f50dbac3 --- /dev/null +++ b/contracts/wasm/erc721/client/go/erc721.go @@ -0,0 +1,19 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package erc721 + +func OnErc721Approval(event *EventApproval) { +} + +func OnErc721ApprovalForAll(event *EventApprovalForAll) { +} + +func OnErc721Init(event *EventInit) { +} + +func OnErc721Mint(event *EventMint) { +} + +func OnErc721Transfer(event *EventTransfer) { +} diff --git a/contracts/wasm/erc721/client/go/events.go b/contracts/wasm/erc721/client/go/events.go new file mode 100644 index 0000000000..c1cb557abd --- /dev/null +++ b/contracts/wasm/erc721/client/go/events.go @@ -0,0 +1,98 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package erc721 + +import ( + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" +) + +var EventHandlers = map[string]func([]string){ + "erc721.approval": onErc721ApprovalThunk, + "erc721.approvalForAll": onErc721ApprovalForAllThunk, + "erc721.init": onErc721InitThunk, + "erc721.mint": onErc721MintThunk, + "erc721.transfer": onErc721TransferThunk, +} + +type EventApproval struct { + client.Event + Approved client.AgentID + Owner client.AgentID + TokenID client.Hash +} + +func onErc721ApprovalThunk(message []string) { + e := &EventApproval{} + e.Init(message) + e.Approved = e.NextAgentID() + e.Owner = e.NextAgentID() + e.TokenID = e.NextHash() + OnErc721Approval(e) +} + +type EventApprovalForAll struct { + client.Event + Approval bool + Operator client.AgentID + Owner client.AgentID +} + +func onErc721ApprovalForAllThunk(message []string) { + e := &EventApprovalForAll{} + e.Init(message) + e.Approval = e.NextBool() + e.Operator = e.NextAgentID() + e.Owner = e.NextAgentID() + OnErc721ApprovalForAll(e) +} + +type EventInit struct { + client.Event + Name string + Symbol string +} + +func onErc721InitThunk(message []string) { + e := &EventInit{} + e.Init(message) + e.Name = e.NextString() + e.Symbol = e.NextString() + OnErc721Init(e) +} + +type EventMint struct { + client.Event + Balance uint64 + Owner client.AgentID + TokenID client.Hash +} + +func onErc721MintThunk(message []string) { + e := &EventMint{} + e.Init(message) + e.Balance = e.NextUint64() + e.Owner = e.NextAgentID() + e.TokenID = e.NextHash() + OnErc721Mint(e) +} + +type EventTransfer struct { + client.Event + From client.AgentID + To client.AgentID + TokenID client.Hash +} + +func onErc721TransferThunk(message []string) { + e := &EventTransfer{} + e.Init(message) + e.From = e.NextAgentID() + e.To = e.NextAgentID() + e.TokenID = e.NextHash() + OnErc721Transfer(e) +} diff --git a/contracts/wasm/erc721/client/go/service.go b/contracts/wasm/erc721/client/go/service.go new file mode 100644 index 0000000000..b3b4f0241a --- /dev/null +++ b/contracts/wasm/erc721/client/go/service.go @@ -0,0 +1,434 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package erc721 + +import ( + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" +) + +const ( + ArgApproval = "approval" + ArgApproved = "approved" + ArgData = "data" + ArgFrom = "from" + ArgName = "n" + ArgOperator = "operator" + ArgOwner = "owner" + ArgSymbol = "s" + ArgTo = "to" + ArgTokenID = "tokenID" + + ResAmount = "amount" + ResApproval = "approval" + ResApproved = "approved" + ResName = "name" + ResOwner = "owner" + ResSymbol = "symbol" + ResTokenURI = "tokenURI" +) + +///////////////////////////// approve ///////////////////////////// + +type ApproveFunc struct { + svc *client.Service + args client.Arguments +} + +func (f ApproveFunc) Approved(v client.AgentID) { + f.args.SetAgentID(ArgApproved, v) +} + +func (f ApproveFunc) TokenID(v client.Hash) { + f.args.SetHash(ArgTokenID, v) +} + +func (f ApproveFunc) Post() { + f.args.Mandatory(ArgApproved) + f.args.Mandatory(ArgTokenID) + f.svc.PostRequest("approve", &f.args) +} + +///////////////////////////// burn ///////////////////////////// + +type BurnFunc struct { + svc *client.Service + args client.Arguments +} + +func (f BurnFunc) TokenID(v client.Hash) { + f.args.SetHash(ArgTokenID, v) +} + +func (f BurnFunc) Post() { + f.args.Mandatory(ArgTokenID) + f.svc.PostRequest("burn", &f.args) +} + +///////////////////////////// init ///////////////////////////// + +type InitFunc struct { + svc *client.Service + args client.Arguments +} + +func (f InitFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f InitFunc) Symbol(v string) { + f.args.SetString(ArgSymbol, v) +} + +func (f InitFunc) Post() { + f.args.Mandatory(ArgName) + f.args.Mandatory(ArgSymbol) + f.svc.PostRequest("init", &f.args) +} + +///////////////////////////// mint ///////////////////////////// + +type MintFunc struct { + svc *client.Service + args client.Arguments +} + +func (f MintFunc) TokenID(v client.Hash) { + f.args.SetHash(ArgTokenID, v) +} + +func (f MintFunc) Post() { + f.args.Mandatory(ArgTokenID) + f.svc.PostRequest("mint", &f.args) +} + +///////////////////////////// safeTransferFrom ///////////////////////////// + +type SafeTransferFromFunc struct { + svc *client.Service + args client.Arguments +} + +func (f SafeTransferFromFunc) Data(v []byte) { + f.args.SetBytes(ArgData, v) +} + +func (f SafeTransferFromFunc) From(v client.AgentID) { + f.args.SetAgentID(ArgFrom, v) +} + +func (f SafeTransferFromFunc) To(v client.AgentID) { + f.args.SetAgentID(ArgTo, v) +} + +func (f SafeTransferFromFunc) TokenID(v client.Hash) { + f.args.SetHash(ArgTokenID, v) +} + +func (f SafeTransferFromFunc) Post() { + f.args.Mandatory(ArgData) + f.args.Mandatory(ArgFrom) + f.args.Mandatory(ArgTo) + f.args.Mandatory(ArgTokenID) + f.svc.PostRequest("safeTransferFrom", &f.args) +} + +///////////////////////////// setApprovalForAll ///////////////////////////// + +type SetApprovalForAllFunc struct { + svc *client.Service + args client.Arguments +} + +func (f SetApprovalForAllFunc) Approval(v bool) { + f.args.SetBool(ArgApproval, v) +} + +func (f SetApprovalForAllFunc) Operator(v client.AgentID) { + f.args.SetAgentID(ArgOperator, v) +} + +func (f SetApprovalForAllFunc) Post() { + f.args.Mandatory(ArgApproval) + f.args.Mandatory(ArgOperator) + f.svc.PostRequest("setApprovalForAll", &f.args) +} + +///////////////////////////// transferFrom ///////////////////////////// + +type TransferFromFunc struct { + svc *client.Service + args client.Arguments +} + +func (f TransferFromFunc) From(v client.AgentID) { + f.args.SetAgentID(ArgFrom, v) +} + +func (f TransferFromFunc) To(v client.AgentID) { + f.args.SetAgentID(ArgTo, v) +} + +func (f TransferFromFunc) TokenID(v client.Hash) { + f.args.SetHash(ArgTokenID, v) +} + +func (f TransferFromFunc) Post() { + f.args.Mandatory(ArgFrom) + f.args.Mandatory(ArgTo) + f.args.Mandatory(ArgTokenID) + f.svc.PostRequest("transferFrom", &f.args) +} + +///////////////////////////// balanceOf ///////////////////////////// + +type BalanceOfView struct { + svc *client.Service + args client.Arguments +} + +func (f BalanceOfView) Owner(v client.AgentID) { + f.args.SetAgentID(ArgOwner, v) +} + +func (f BalanceOfView) Call() BalanceOfResults { + f.args.Mandatory(ArgOwner) + return BalanceOfResults{res: f.svc.CallView("balanceOf", &f.args)} +} + +type BalanceOfResults struct { + res client.Results +} + +func (r BalanceOfResults) AmountExists() bool { + return r.res.Exists(ResAmount) +} + +func (r BalanceOfResults) Amount() uint64 { + return r.res.GetUint64(ResAmount) +} + +///////////////////////////// getApproved ///////////////////////////// + +type GetApprovedView struct { + svc *client.Service + args client.Arguments +} + +func (f GetApprovedView) TokenID(v client.Hash) { + f.args.SetHash(ArgTokenID, v) +} + +func (f GetApprovedView) Call() GetApprovedResults { + f.args.Mandatory(ArgTokenID) + return GetApprovedResults{res: f.svc.CallView("getApproved", &f.args)} +} + +type GetApprovedResults struct { + res client.Results +} + +func (r GetApprovedResults) ApprovedExists() bool { + return r.res.Exists(ResApproved) +} + +func (r GetApprovedResults) Approved() client.AgentID { + return r.res.GetAgentID(ResApproved) +} + +///////////////////////////// isApprovedForAll ///////////////////////////// + +type IsApprovedForAllView struct { + svc *client.Service + args client.Arguments +} + +func (f IsApprovedForAllView) Operator(v client.AgentID) { + f.args.SetAgentID(ArgOperator, v) +} + +func (f IsApprovedForAllView) Owner(v client.AgentID) { + f.args.SetAgentID(ArgOwner, v) +} + +func (f IsApprovedForAllView) Call() IsApprovedForAllResults { + f.args.Mandatory(ArgOperator) + f.args.Mandatory(ArgOwner) + return IsApprovedForAllResults{res: f.svc.CallView("isApprovedForAll", &f.args)} +} + +type IsApprovedForAllResults struct { + res client.Results +} + +func (r IsApprovedForAllResults) ApprovalExists() bool { + return r.res.Exists(ResApproval) +} + +func (r IsApprovedForAllResults) Approval() bool { + return r.res.GetBool(ResApproval) +} + +///////////////////////////// name ///////////////////////////// + +type NameView struct { + svc *client.Service +} + +func (f NameView) Call() NameResults { + return NameResults{res: f.svc.CallView("name", nil)} +} + +type NameResults struct { + res client.Results +} + +func (r NameResults) Name() string { + return r.res.GetString(ResName) +} + +///////////////////////////// ownerOf ///////////////////////////// + +type OwnerOfView struct { + svc *client.Service + args client.Arguments +} + +func (f OwnerOfView) TokenID(v client.Hash) { + f.args.SetHash(ArgTokenID, v) +} + +func (f OwnerOfView) Call() OwnerOfResults { + f.args.Mandatory(ArgTokenID) + return OwnerOfResults{res: f.svc.CallView("ownerOf", &f.args)} +} + +type OwnerOfResults struct { + res client.Results +} + +func (r OwnerOfResults) OwnerExists() bool { + return r.res.Exists(ResOwner) +} + +func (r OwnerOfResults) Owner() client.AgentID { + return r.res.GetAgentID(ResOwner) +} + +///////////////////////////// symbol ///////////////////////////// + +type SymbolView struct { + svc *client.Service +} + +func (f SymbolView) Call() SymbolResults { + return SymbolResults{res: f.svc.CallView("symbol", nil)} +} + +type SymbolResults struct { + res client.Results +} + +func (r SymbolResults) Symbol() string { + return r.res.GetString(ResSymbol) +} + +///////////////////////////// tokenURI ///////////////////////////// + +type TokenURIView struct { + svc *client.Service + args client.Arguments +} + +func (f TokenURIView) TokenID(v client.Hash) { + f.args.SetHash(ArgTokenID, v) +} + +func (f TokenURIView) Call() TokenURIResults { + f.args.Mandatory(ArgTokenID) + return TokenURIResults{res: f.svc.CallView("tokenURI", &f.args)} +} + +type TokenURIResults struct { + res client.Results +} + +func (r TokenURIResults) TokenURIExists() bool { + return r.res.Exists(ResTokenURI) +} + +func (r TokenURIResults) TokenURI() string { + return r.res.GetString(ResTokenURI) +} + +///////////////////////////// Erc721Service ///////////////////////////// + +type Erc721Service struct { + client.Service +} + +func NewErc721Service(cl client.ServiceClient, chainID string) *Erc721Service { + s := &Erc721Service{} + s.Service.Init(cl, chainID, "d967c216", EventHandlers) + return s +} + +func (s *Erc721Service) Approve() ApproveFunc { + return ApproveFunc{svc: &s.Service} +} + +func (s *Erc721Service) Burn() BurnFunc { + return BurnFunc{svc: &s.Service} +} + +func (s *Erc721Service) Init() InitFunc { + return InitFunc{svc: &s.Service} +} + +func (s *Erc721Service) Mint() MintFunc { + return MintFunc{svc: &s.Service} +} + +func (s *Erc721Service) SafeTransferFrom() SafeTransferFromFunc { + return SafeTransferFromFunc{svc: &s.Service} +} + +func (s *Erc721Service) SetApprovalForAll() SetApprovalForAllFunc { + return SetApprovalForAllFunc{svc: &s.Service} +} + +func (s *Erc721Service) TransferFrom() TransferFromFunc { + return TransferFromFunc{svc: &s.Service} +} + +func (s *Erc721Service) BalanceOf() BalanceOfView { + return BalanceOfView{svc: &s.Service} +} + +func (s *Erc721Service) GetApproved() GetApprovedView { + return GetApprovedView{svc: &s.Service} +} + +func (s *Erc721Service) IsApprovedForAll() IsApprovedForAllView { + return IsApprovedForAllView{svc: &s.Service} +} + +func (s *Erc721Service) Name() NameView { + return NameView{svc: &s.Service} +} + +func (s *Erc721Service) OwnerOf() OwnerOfView { + return OwnerOfView{svc: &s.Service} +} + +func (s *Erc721Service) Symbol() SymbolView { + return SymbolView{svc: &s.Service} +} + +func (s *Erc721Service) TokenURI() TokenURIView { + return TokenURIView{svc: &s.Service} +} diff --git a/contracts/wasm/erc721/client/ts/erc721.ts b/contracts/wasm/erc721/client/ts/erc721.ts new file mode 100644 index 0000000000..2c0b480a78 --- /dev/null +++ b/contracts/wasm/erc721/client/ts/erc721.ts @@ -0,0 +1,23 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as events from "./events" +import * as service from "./service" + +const client = new BasicClient(config); +const erc721Service = new service.Erc721Service(client, config.ChainId); + +export function onErc721Approval(event: events.EventApproval): void { +} + +export function onErc721ApprovalForAll(event: events.EventApprovalForAll): void { +} + +export function onErc721Init(event: events.EventInit): void { +} + +export function onErc721Mint(event: events.EventMint): void { +} + +export function onErc721Transfer(event: events.EventTransfer): void { +} diff --git a/contracts/wasm/erc721/client/ts/events.ts b/contracts/wasm/erc721/client/ts/events.ts new file mode 100644 index 0000000000..f3abe2bd1e --- /dev/null +++ b/contracts/wasm/erc721/client/ts/events.ts @@ -0,0 +1,85 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as client from "wasmlib/client" +import * as app from "./erc721" + +export const eventHandlers: client.EventHandlers = { + "erc721.approval": onErc721ApprovalThunk, + "erc721.approvalForAll": onErc721ApprovalForAllThunk, + "erc721.init": onErc721InitThunk, + "erc721.mint": onErc721MintThunk, + "erc721.transfer": onErc721TransferThunk, +}; + +export class EventApproval extends client.Event { + public approved: client.AgentID; + public owner: client.AgentID; + public tokenID: client.Hash; +} + +function onErc721ApprovalThunk(message: string[]) { + let e = new EventApproval(message); + e.approved = e.nextAgentID(); + e.owner = e.nextAgentID(); + e.tokenID = e.nextHash(); + app.onErc721Approval(e); +} + +export class EventApprovalForAll extends client.Event { + public approval: client.Bool; + public operator: client.AgentID; + public owner: client.AgentID; +} + +function onErc721ApprovalForAllThunk(message: string[]) { + let e = new EventApprovalForAll(message); + e.approval = e.nextBool(); + e.operator = e.nextAgentID(); + e.owner = e.nextAgentID(); + app.onErc721ApprovalForAll(e); +} + +export class EventInit extends client.Event { + public name: client.String; + public symbol: client.String; +} + +function onErc721InitThunk(message: string[]) { + let e = new EventInit(message); + e.name = e.nextString(); + e.symbol = e.nextString(); + app.onErc721Init(e); +} + +export class EventMint extends client.Event { + public balance: client.Uint64; + public owner: client.AgentID; + public tokenID: client.Hash; +} + +function onErc721MintThunk(message: string[]) { + let e = new EventMint(message); + e.balance = e.nextUint64(); + e.owner = e.nextAgentID(); + e.tokenID = e.nextHash(); + app.onErc721Mint(e); +} + +export class EventTransfer extends client.Event { + public from: client.AgentID; + public to: client.AgentID; + public tokenID: client.Hash; +} + +function onErc721TransferThunk(message: string[]) { + let e = new EventTransfer(message); + e.from = e.nextAgentID(); + e.to = e.nextAgentID(); + e.tokenID = e.nextHash(); + app.onErc721Transfer(e); +} diff --git a/contracts/wasm/erc721/client/ts/index.ts b/contracts/wasm/erc721/client/ts/index.ts new file mode 100644 index 0000000000..84d225f2ab --- /dev/null +++ b/contracts/wasm/erc721/client/ts/index.ts @@ -0,0 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from "./erc721"; +export * from "./events"; +export * from "./service"; diff --git a/contracts/wasm/erc721/client/ts/service.ts b/contracts/wasm/erc721/client/ts/service.ts new file mode 100644 index 0000000000..39095541a1 --- /dev/null +++ b/contracts/wasm/erc721/client/ts/service.ts @@ -0,0 +1,405 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as client from "wasmlib/client" +import * as events from "./events" + +const ArgApproval = "approval"; +const ArgApproved = "approved"; +const ArgData = "data"; +const ArgFrom = "from"; +const ArgName = "n"; +const ArgOperator = "operator"; +const ArgOwner = "owner"; +const ArgSymbol = "s"; +const ArgTo = "to"; +const ArgTokenID = "tokenID"; + +const ResAmount = "amount"; +const ResApproval = "approval"; +const ResApproved = "approved"; +const ResName = "name"; +const ResOwner = "owner"; +const ResSymbol = "symbol"; +const ResTokenURI = "tokenURI"; + +///////////////////////////// approve ///////////////////////////// + +export class ApproveFunc extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + approved(v: client.AgentID): void { + this.args.setAgentID(ArgApproved, v); + } + + tokenID(v: client.Hash): void { + this.args.setHash(ArgTokenID, v); + } + + public async post(): Promise { + this.args.mandatory(ArgApproved); + this.args.mandatory(ArgTokenID); + this.svc.postRequest("approve", this.args); + } +} + +///////////////////////////// burn ///////////////////////////// + +export class BurnFunc extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + tokenID(v: client.Hash): void { + this.args.setHash(ArgTokenID, v); + } + + public async post(): Promise { + this.args.mandatory(ArgTokenID); + this.svc.postRequest("burn", this.args); + } +} + +///////////////////////////// init ///////////////////////////// + +export class InitFunc extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + name(v: string): void { + this.args.setString(ArgName, v); + } + + symbol(v: string): void { + this.args.setString(ArgSymbol, v); + } + + public async post(): Promise { + this.args.mandatory(ArgName); + this.args.mandatory(ArgSymbol); + this.svc.postRequest("init", this.args); + } +} + +///////////////////////////// mint ///////////////////////////// + +export class MintFunc extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + tokenID(v: client.Hash): void { + this.args.setHash(ArgTokenID, v); + } + + public async post(): Promise { + this.args.mandatory(ArgTokenID); + this.svc.postRequest("mint", this.args); + } +} + +///////////////////////////// safeTransferFrom ///////////////////////////// + +export class SafeTransferFromFunc extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + data(v: client.Bytes): void { + this.args.setBytes(ArgData, v); + } + + from(v: client.AgentID): void { + this.args.setAgentID(ArgFrom, v); + } + + to(v: client.AgentID): void { + this.args.setAgentID(ArgTo, v); + } + + tokenID(v: client.Hash): void { + this.args.setHash(ArgTokenID, v); + } + + public async post(): Promise { + this.args.mandatory(ArgData); + this.args.mandatory(ArgFrom); + this.args.mandatory(ArgTo); + this.args.mandatory(ArgTokenID); + this.svc.postRequest("safeTransferFrom", this.args); + } +} + +///////////////////////////// setApprovalForAll ///////////////////////////// + +export class SetApprovalForAllFunc extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + approval(v: boolean): void { + this.args.setBool(ArgApproval, v); + } + + operator(v: client.AgentID): void { + this.args.setAgentID(ArgOperator, v); + } + + public async post(): Promise { + this.args.mandatory(ArgApproval); + this.args.mandatory(ArgOperator); + this.svc.postRequest("setApprovalForAll", this.args); + } +} + +///////////////////////////// transferFrom ///////////////////////////// + +export class TransferFromFunc extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + from(v: client.AgentID): void { + this.args.setAgentID(ArgFrom, v); + } + + to(v: client.AgentID): void { + this.args.setAgentID(ArgTo, v); + } + + tokenID(v: client.Hash): void { + this.args.setHash(ArgTokenID, v); + } + + public async post(): Promise { + this.args.mandatory(ArgFrom); + this.args.mandatory(ArgTo); + this.args.mandatory(ArgTokenID); + this.svc.postRequest("transferFrom", this.args); + } +} + +///////////////////////////// balanceOf ///////////////////////////// + +export class BalanceOfView extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + owner(v: client.AgentID): void { + this.args.setAgentID(ArgOwner, v); + } + + public async call(): Promise { + this.args.mandatory(ArgOwner); + return new BalanceOfResults(this.svc.callView("balanceOf", this.args)); + } +} + +export class BalanceOfResults extends client.ViewResults { + + amountExists(): boolean { + return this.res.exists(ResAmount) + } + + amount(): client.Uint64 { + return this.res.getUint64(ResAmount); + } +} + +///////////////////////////// getApproved ///////////////////////////// + +export class GetApprovedView extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + tokenID(v: client.Hash): void { + this.args.setHash(ArgTokenID, v); + } + + public async call(): Promise { + this.args.mandatory(ArgTokenID); + return new GetApprovedResults(this.svc.callView("getApproved", this.args)); + } +} + +export class GetApprovedResults extends client.ViewResults { + + approvedExists(): boolean { + return this.res.exists(ResApproved) + } + + approved(): client.AgentID { + return this.res.getAgentID(ResApproved); + } +} + +///////////////////////////// isApprovedForAll ///////////////////////////// + +export class IsApprovedForAllView extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + operator(v: client.AgentID): void { + this.args.setAgentID(ArgOperator, v); + } + + owner(v: client.AgentID): void { + this.args.setAgentID(ArgOwner, v); + } + + public async call(): Promise { + this.args.mandatory(ArgOperator); + this.args.mandatory(ArgOwner); + return new IsApprovedForAllResults(this.svc.callView("isApprovedForAll", this.args)); + } +} + +export class IsApprovedForAllResults extends client.ViewResults { + + approvalExists(): boolean { + return this.res.exists(ResApproval) + } + + approval(): boolean { + return this.res.getBool(ResApproval); + } +} + +///////////////////////////// name ///////////////////////////// + +export class NameView extends client.FuncObject { + + public async call(): Promise { + return new NameResults(this.svc.callView("name", null)); + } +} + +export class NameResults extends client.ViewResults { + + name(): string { + return this.res.getString(ResName); + } +} + +///////////////////////////// ownerOf ///////////////////////////// + +export class OwnerOfView extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + tokenID(v: client.Hash): void { + this.args.setHash(ArgTokenID, v); + } + + public async call(): Promise { + this.args.mandatory(ArgTokenID); + return new OwnerOfResults(this.svc.callView("ownerOf", this.args)); + } +} + +export class OwnerOfResults extends client.ViewResults { + + ownerExists(): boolean { + return this.res.exists(ResOwner) + } + + owner(): client.AgentID { + return this.res.getAgentID(ResOwner); + } +} + +///////////////////////////// symbol ///////////////////////////// + +export class SymbolView extends client.FuncObject { + + public async call(): Promise { + return new SymbolResults(this.svc.callView("symbol", null)); + } +} + +export class SymbolResults extends client.ViewResults { + + symbol(): string { + return this.res.getString(ResSymbol); + } +} + +///////////////////////////// tokenURI ///////////////////////////// + +export class TokenURIView extends client.FuncObject { + args: client.Arguments = new client.Arguments(); + + tokenID(v: client.Hash): void { + this.args.setHash(ArgTokenID, v); + } + + public async call(): Promise { + this.args.mandatory(ArgTokenID); + return new TokenURIResults(this.svc.callView("tokenURI", this.args)); + } +} + +export class TokenURIResults extends client.ViewResults { + + tokenURIExists(): boolean { + return this.res.exists(ResTokenURI) + } + + tokenURI(): string { + return this.res.getString(ResTokenURI); + } +} + +///////////////////////////// Erc721Service ///////////////////////////// + +export class Erc721Service extends client.Service { + + constructor(cl: client.ServiceClient, chainID: string) { + super(cl, chainID, "d967c216", events.eventHandlers); + } + + public approve(): ApproveFunc { + return new ApproveFunc(this); + } + + public burn(): BurnFunc { + return new BurnFunc(this); + } + + public init(): InitFunc { + return new InitFunc(this); + } + + public mint(): MintFunc { + return new MintFunc(this); + } + + public safeTransferFrom(): SafeTransferFromFunc { + return new SafeTransferFromFunc(this); + } + + public setApprovalForAll(): SetApprovalForAllFunc { + return new SetApprovalForAllFunc(this); + } + + public transferFrom(): TransferFromFunc { + return new TransferFromFunc(this); + } + + public balanceOf(): BalanceOfView { + return new BalanceOfView(this); + } + + public getApproved(): GetApprovedView { + return new GetApprovedView(this); + } + + public isApprovedForAll(): IsApprovedForAllView { + return new IsApprovedForAllView(this); + } + + public name(): NameView { + return new NameView(this); + } + + public ownerOf(): OwnerOfView { + return new OwnerOfView(this); + } + + public symbol(): SymbolView { + return new SymbolView(this); + } + + public tokenURI(): TokenURIView { + return new TokenURIView(this); + } +} diff --git a/contracts/wasm/erc721/client/ts/tsconfig.json b/contracts/wasm/erc721/client/ts/tsconfig.json new file mode 100644 index 0000000000..b5353d085d --- /dev/null +++ b/contracts/wasm/erc721/client/ts/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es2020"], + "target": "es2020", + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], +} \ No newline at end of file diff --git a/contracts/wasm/fairroulette/client/go/events.go b/contracts/wasm/fairroulette/client/go/events.go new file mode 100644 index 0000000000..cdf2c0b077 --- /dev/null +++ b/contracts/wasm/fairroulette/client/go/events.go @@ -0,0 +1,95 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package fairroulette + +import ( + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" +) + +var EventHandlers = map[string]func([]string){ + "fairroulette.bet": onFairRouletteBetThunk, + "fairroulette.payout": onFairRoulettePayoutThunk, + "fairroulette.round": onFairRouletteRoundThunk, + "fairroulette.start": onFairRouletteStartThunk, + "fairroulette.stop": onFairRouletteStopThunk, + "fairroulette.winner": onFairRouletteWinnerThunk, +} + +type EventBet struct { + client.Event + Address client.Address + Amount int64 + Number int64 +} + +func onFairRouletteBetThunk(message []string) { + e := &EventBet{} + e.Init(message) + e.Address = e.NextAddress() + e.Amount = e.NextInt64() + e.Number = e.NextInt64() + OnFairRouletteBet(e) +} + +type EventPayout struct { + client.Event + Address client.Address + Amount int64 +} + +func onFairRoulettePayoutThunk(message []string) { + e := &EventPayout{} + e.Init(message) + e.Address = e.NextAddress() + e.Amount = e.NextInt64() + OnFairRoulettePayout(e) +} + +type EventRound struct { + client.Event + Number int64 +} + +func onFairRouletteRoundThunk(message []string) { + e := &EventRound{} + e.Init(message) + e.Number = e.NextInt64() + OnFairRouletteRound(e) +} + +type EventStart struct { + client.Event +} + +func onFairRouletteStartThunk(message []string) { + e := &EventStart{} + e.Init(message) + OnFairRouletteStart(e) +} + +type EventStop struct { + client.Event +} + +func onFairRouletteStopThunk(message []string) { + e := &EventStop{} + e.Init(message) + OnFairRouletteStop(e) +} + +type EventWinner struct { + client.Event + Number int64 +} + +func onFairRouletteWinnerThunk(message []string) { + e := &EventWinner{} + e.Init(message) + e.Number = e.NextInt64() + OnFairRouletteWinner(e) +} diff --git a/contracts/wasm/fairroulette/client/go/fairroulette.go b/contracts/wasm/fairroulette/client/go/fairroulette.go new file mode 100644 index 0000000000..0f3e9ce2f6 --- /dev/null +++ b/contracts/wasm/fairroulette/client/go/fairroulette.go @@ -0,0 +1,22 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package fairroulette + +func OnFairRouletteBet(event *EventBet) { +} + +func OnFairRoulettePayout(event *EventPayout) { +} + +func OnFairRouletteRound(event *EventRound) { +} + +func OnFairRouletteStart(event *EventStart) { +} + +func OnFairRouletteStop(event *EventStop) { +} + +func OnFairRouletteWinner(event *EventWinner) { +} diff --git a/contracts/wasm/fairroulette/client/go/service.go b/contracts/wasm/fairroulette/client/go/service.go new file mode 100644 index 0000000000..13053b6620 --- /dev/null +++ b/contracts/wasm/fairroulette/client/go/service.go @@ -0,0 +1,172 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package fairroulette + +import ( + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" +) + +const ( + ArgNumber = "number" + ArgPlayPeriod = "playPeriod" + + ResLastWinningNumber = "lastWinningNumber" + ResRoundNumber = "roundNumber" + ResRoundStartedAt = "roundStartedAt" + ResRoundStatus = "roundStatus" +) + +type ForcePayoutFunc struct{} + +func (f ForcePayoutFunc) Post() { + // TODO DoPost(nil) +} + +type ForceResetFunc struct{} + +func (f ForceResetFunc) Post() { + // TODO DoPost(nil) +} + +type PayWinnersFunc struct{} + +func (f PayWinnersFunc) Post() { + // TODO DoPost(nil) +} + +type PlaceBetFunc struct { + args client.Arguments +} + +func (f PlaceBetFunc) Number(v int64) { + f.args.SetInt64(ArgNumber, v) +} + +func (f PlaceBetFunc) Post() { + f.args.Mandatory(ArgNumber) + // TODO DoPost(f.args) +} + +type PlayPeriodFunc struct { + args client.Arguments +} + +func (f PlayPeriodFunc) PlayPeriod(v int32) { + f.args.SetInt32(ArgPlayPeriod, v) +} + +func (f PlayPeriodFunc) Post() { + f.args.Mandatory(ArgPlayPeriod) + // TODO DoPost(f.args) +} + +type LastWinningNumberView struct{} + +func (f LastWinningNumberView) Call() LastWinningNumberResults { + // TODO DoCall(nil) instead of client.NewResults() + return LastWinningNumberResults{res: client.NewResults()} +} + +type LastWinningNumberResults struct { + res client.Results +} + +func (r LastWinningNumberResults) LastWinningNumber() int64 { + return r.res.GetInt64(ResLastWinningNumber) +} + +type RoundNumberView struct{} + +func (f RoundNumberView) Call() RoundNumberResults { + // TODO DoCall(nil) instead of client.NewResults() + return RoundNumberResults{res: client.NewResults()} +} + +type RoundNumberResults struct { + res client.Results +} + +func (r RoundNumberResults) RoundNumber() int64 { + return r.res.GetInt64(ResRoundNumber) +} + +type RoundStartedAtView struct{} + +func (f RoundStartedAtView) Call() RoundStartedAtResults { + // TODO DoCall(nil) instead of client.NewResults() + return RoundStartedAtResults{res: client.NewResults()} +} + +type RoundStartedAtResults struct { + res client.Results +} + +func (r RoundStartedAtResults) RoundStartedAt() int32 { + return r.res.GetInt32(ResRoundStartedAt) +} + +type RoundStatusView struct{} + +func (f RoundStatusView) Call() RoundStatusResults { + // TODO DoCall(nil) instead of client.NewResults() + return RoundStatusResults{res: client.NewResults()} +} + +type RoundStatusResults struct { + res client.Results +} + +func (r RoundStatusResults) RoundStatus() int16 { + return r.res.GetInt16(ResRoundStatus) +} + +type FairRouletteService struct { + client.Service +} + +func NewFairRouletteService(cl client.ServiceClient, chainID string) *FairRouletteService { + s := &FairRouletteService{} + s.Service.Init(cl, chainID, "df79d138", EventHandlers) + return s +} + +func (s *FairRouletteService) ForcePayout() ForcePayoutFunc { + return ForcePayoutFunc{} +} + +func (s *FairRouletteService) ForceReset() ForceResetFunc { + return ForceResetFunc{} +} + +func (s *FairRouletteService) PayWinners() PayWinnersFunc { + return PayWinnersFunc{} +} + +func (s *FairRouletteService) PlaceBet() PlaceBetFunc { + return PlaceBetFunc{} +} + +func (s *FairRouletteService) PlayPeriod() PlayPeriodFunc { + return PlayPeriodFunc{} +} + +func (s *FairRouletteService) LastWinningNumber() LastWinningNumberView { + return LastWinningNumberView{} +} + +func (s *FairRouletteService) RoundNumber() RoundNumberView { + return RoundNumberView{} +} + +func (s *FairRouletteService) RoundStartedAt() RoundStartedAtView { + return RoundStartedAtView{} +} + +func (s *FairRouletteService) RoundStatus() RoundStatusView { + return RoundStatusView{} +} diff --git a/contracts/wasm/fairroulette/client/ts/events.ts b/contracts/wasm/fairroulette/client/ts/events.ts new file mode 100644 index 0000000000..4d6bc1f216 --- /dev/null +++ b/contracts/wasm/fairroulette/client/ts/events.ts @@ -0,0 +1,80 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as client from "wasmlib/client" +import * as app from "./fairroulette" + +export const eventHandlers: client.EventHandlers = { + "fairroulette.bet": onFairRouletteBetThunk, + "fairroulette.payout": onFairRoulettePayoutThunk, + "fairroulette.round": onFairRouletteRoundThunk, + "fairroulette.start": onFairRouletteStartThunk, + "fairroulette.stop": onFairRouletteStopThunk, + "fairroulette.winner": onFairRouletteWinnerThunk, +}; + +export class EventBet extends client.Event { + public address: client.Address; + public amount: client.Int64; + public number: client.Int64; +} + +function onFairRouletteBetThunk(message: string[]) { + let e = new EventBet(message); + e.address = e.nextAddress(); + e.amount = e.nextInt64(); + e.number = e.nextInt64(); + app.onFairRouletteBet(e); +} + +export class EventPayout extends client.Event { + public address: client.Address; + public amount: client.Int64; +} + +function onFairRoulettePayoutThunk(message: string[]) { + let e = new EventPayout(message); + e.address = e.nextAddress(); + e.amount = e.nextInt64(); + app.onFairRoulettePayout(e); +} + +export class EventRound extends client.Event { + public number: client.Int64; +} + +function onFairRouletteRoundThunk(message: string[]) { + let e = new EventRound(message); + e.number = e.nextInt64(); + app.onFairRouletteRound(e); +} + +export class EventStart extends client.Event { +} + +function onFairRouletteStartThunk(message: string[]) { + let e = new EventStart(message); + app.onFairRouletteStart(e); +} + +export class EventStop extends client.Event { +} + +function onFairRouletteStopThunk(message: string[]) { + let e = new EventStop(message); + app.onFairRouletteStop(e); +} + +export class EventWinner extends client.Event { + public number: client.Int64; +} + +function onFairRouletteWinnerThunk(message: string[]) { + let e = new EventWinner(message); + e.number = e.nextInt64(); + app.onFairRouletteWinner(e); +} diff --git a/contracts/wasm/fairroulette/client/ts/fairroulette.ts b/contracts/wasm/fairroulette/client/ts/fairroulette.ts new file mode 100644 index 0000000000..45895bb311 --- /dev/null +++ b/contracts/wasm/fairroulette/client/ts/fairroulette.ts @@ -0,0 +1,26 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as events from "./events" +import * as service from "./service" + +const client = new BasicClient(config); +const fairRouletteService = new service.FairRouletteService(client, config.ChainId); + +export function onFairRouletteBet(event: events.EventBet): void { +} + +export function onFairRoulettePayout(event: events.EventPayout): void { +} + +export function onFairRouletteRound(event: events.EventRound): void { +} + +export function onFairRouletteStart(event: events.EventStart): void { +} + +export function onFairRouletteStop(event: events.EventStop): void { +} + +export function onFairRouletteWinner(event: events.EventWinner): void { +} diff --git a/contracts/wasm/fairroulette/client/ts/service.ts b/contracts/wasm/fairroulette/client/ts/service.ts new file mode 100644 index 0000000000..fa9d42792f --- /dev/null +++ b/contracts/wasm/fairroulette/client/ts/service.ts @@ -0,0 +1,179 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as client from "wasmlib/client" +import * as events from "./events" + +const ArgNumber = "number"; +const ArgPlayPeriod = "playPeriod"; + +const ResLastWinningNumber = "lastWinningNumber"; +const ResRoundNumber = "roundNumber"; +const ResRoundStartedAt = "roundStartedAt"; +const ResRoundStatus = "roundStatus"; + +export class ForcePayoutFunc { + + post(): void { + //TODO DoPost(null) + } +} + +export class ForceResetFunc { + + post(): void { + //TODO DoPost(null) + } +} + +export class PayWinnersFunc { + + post(): void { + //TODO DoPost(null) + } +} + +export class PlaceBetFunc { + args: client.Arguments = new client.Arguments(); + + number(v: client.Int64): void { + this.args.setInt64(ArgNumber, v); + } + + post(): void { + this.args.mandatory(ArgNumber); + //TODO DoPost(this.args) + } +} + +export class PlayPeriodFunc { + args: client.Arguments = new client.Arguments(); + + playPeriod(v: client.Int32): void { + this.args.setInt32(ArgPlayPeriod, v); + } + + post(): void { + this.args.mandatory(ArgPlayPeriod); + //TODO DoPost(this.args) + } +} + +export class LastWinningNumberView { + + call(): LastWinningNumberResults { + //TODO DoCall(null) instead of new client.Results() + return new LastWinningNumberResults(new client.Results()); + } +} + +export class LastWinningNumberResults { + res: client.Results; + + constructor(res: client.Results) { this.res = res; } + + lastWinningNumber(): client.Int64 { + return this.res.getInt64(ResLastWinningNumber); + } +} + +export class RoundNumberView { + + call(): RoundNumberResults { + //TODO DoCall(null) instead of new client.Results() + return new RoundNumberResults(new client.Results()); + } +} + +export class RoundNumberResults { + res: client.Results; + + constructor(res: client.Results) { this.res = res; } + + roundNumber(): client.Int64 { + return this.res.getInt64(ResRoundNumber); + } +} + +export class RoundStartedAtView { + + call(): RoundStartedAtResults { + //TODO DoCall(null) instead of new client.Results() + return new RoundStartedAtResults(new client.Results()); + } +} + +export class RoundStartedAtResults { + res: client.Results; + + constructor(res: client.Results) { this.res = res; } + + roundStartedAt(): client.Int32 { + return this.res.getInt32(ResRoundStartedAt); + } +} + +export class RoundStatusView { + + call(): RoundStatusResults { + //TODO DoCall(null) instead of new client.Results() + return new RoundStatusResults(new client.Results()); + } +} + +export class RoundStatusResults { + res: client.Results; + + constructor(res: client.Results) { this.res = res; } + + roundStatus(): client.Int16 { + return this.res.getInt16(ResRoundStatus); + } +} + +export class FairRouletteService extends client.Service { + + constructor(cl: client.ServiceClient, chainID: string) { + super(cl, chainID, "df79d138", events.eventHandlers); + } + + public forcePayout(): ForcePayoutFunc { + return new ForcePayoutFunc(); + } + + public forceReset(): ForceResetFunc { + return new ForceResetFunc(); + } + + public payWinners(): PayWinnersFunc { + return new PayWinnersFunc(); + } + + public placeBet(): PlaceBetFunc { + return new PlaceBetFunc(); + } + + public playPeriod(): PlayPeriodFunc { + return new PlayPeriodFunc(); + } + + public lastWinningNumber(): LastWinningNumberView { + return new LastWinningNumberView(); + } + + public roundNumber(): RoundNumberView { + return new RoundNumberView(); + } + + public roundStartedAt(): RoundStartedAtView { + return new RoundStartedAtView(); + } + + public roundStatus(): RoundStatusView { + return new RoundStatusView(); + } +} diff --git a/contracts/wasm/fairroulette/client/ts/tsconfig.json b/contracts/wasm/fairroulette/client/ts/tsconfig.json new file mode 100644 index 0000000000..b5353d085d --- /dev/null +++ b/contracts/wasm/fairroulette/client/ts/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es2020"], + "target": "es2020", + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], +} \ No newline at end of file diff --git a/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts b/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts index 8c9e21baf0..61bb988f02 100644 --- a/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts +++ b/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts @@ -62,11 +62,11 @@ export class FairRouletteService { private handleVmMessage(message: string[]): void { const messageHandlers: MessageHandlers = { - 'fairroulette.bet': (index) => { + 'fairroulette.bet': () => { const bet: Bet = { - better: message[index + 2], - amount: Number(message[index + 3]), - betNumber: Number(message[index + 4]), + better: message[2], + amount: Number(message[3]), + betNumber: Number(message[4]), }; this.emitter.emit('betPlaced', bet); @@ -74,8 +74,8 @@ export class FairRouletteService { 'fairroulette.payout': (index) => { const bet: Bet = { - better: message[index + 2], - amount: Number(message[index + 3]), + better: message[2], + amount: Number(message[3]), betNumber: undefined, }; @@ -83,11 +83,11 @@ export class FairRouletteService { }, 'fairroulette.round': (index) => { - this.emitter.emit('roundNumber', message[index + 2] || 0); + this.emitter.emit('roundNumber', message[2] || 0); }, 'fairroulette.start': (index) => { - this.emitter.emit('roundStarted', message[index + 1] || 0); + this.emitter.emit('roundStarted', message[1] || 0); }, 'fairroulette.stop': (index) => { @@ -95,30 +95,24 @@ export class FairRouletteService { }, 'fairroulette.winner': (index) => { - this.emitter.emit('winningNumber', message[index + 2] || 0); + this.emitter.emit('winningNumber', message[2] || 0); }, }; - const topicIndex = 3; - const topic = message[topicIndex]; - + const topic = message[0]; if (typeof messageHandlers[topic] != 'undefined') { - messageHandlers[topic](topicIndex); + messageHandlers[topic](0); } } private handleIncomingMessage(message: MessageEvent): void { - const msg = message.data.toString().split('|'); - - if (msg.length == 0) { - return; - } - - if (msg[0] != 'vmmsg') { + // expect vmmsg contract.event|param1|param2|... + const msg = message.data.toString().split(' '); + if (msg.length != 4 || msg[0] != 'vmmsg') { return; } - - this.handleVmMessage(msg); + const topics = msg[3].split('|'); + this.handleVmMessage(topics); } public async placeBetOffLedger(keyPair: IKeyPair, betNumber: number, take: bigint): Promise { diff --git a/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/basic_client.ts b/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/basic_client.ts index 25587cac9f..5d8c7fcaf3 100644 --- a/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/basic_client.ts +++ b/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/basic_client.ts @@ -77,11 +77,7 @@ export class BasicClient { public async callView(chainId: string, contractHName: string, entryPoint: string): Promise { const url = `chain/${chainId}/contract/${contractHName}/callview/${entryPoint}`; - const result = await this.sendRequestExt( - this.configuration.WaspAPIUrl, - 'get', - url - ); + const result = await this.sendRequestExt(this.configuration.WaspAPIUrl, 'get', url); return result.body; } diff --git a/packages/vm/wasmlib/go/wasmlib/client/service.go b/packages/vm/wasmlib/go/wasmlib/client/service.go index 200d1c7d1e..ea63c48800 100644 --- a/packages/vm/wasmlib/go/wasmlib/client/service.go +++ b/packages/vm/wasmlib/go/wasmlib/client/service.go @@ -10,14 +10,14 @@ type Response map[string][]byte type Service struct{} -func (s *Service) Init(client ServiceClient, chainId string, scHname string, eventHandlers map[string]func([]string)) { +func (s *Service) Init(cl ServiceClient, chainID, scHname string, eventHandlers map[string]func([]string)) { } -func (s *Service) CallView(viewName string, args map[string][]byte) Response { - return nil +func (s *Service) CallView(viewName string, args *Arguments) Results { + return Results{} } -func (s *Service) PostRequest(hFuncName string, args map[string][]byte) { +func (s *Service) PostRequest(funcName string, args *Arguments) { } ///////////////////////////////////////////////////////////////// diff --git a/packages/vm/wasmlib/ts/wasmlib/client/service.ts b/packages/vm/wasmlib/ts/wasmlib/client/service.ts index 9bc87cdcb4..fb62226103 100644 --- a/packages/vm/wasmlib/ts/wasmlib/client/service.ts +++ b/packages/vm/wasmlib/ts/wasmlib/client/service.ts @@ -10,15 +10,31 @@ export type ServiceClient = wasp.BasicClient; export type EventHandlers = { [key: string]: (message: string[]) => void }; +export class FuncObject { + svc: Service; + + constructor(svc: Service) { + this.svc = svc; + } +} + +export class ViewResults { + res: client.Results; + + constructor(res: client.Results) { + this.res = res; + } +} + export class Service { private client: ServiceClient; private walletService: wasp.WalletService; private webSocket: WebSocket; private eventHandlers: EventHandlers; public chainId: string; - public scHname: string; + public scHname: client.Hname; - constructor(client: ServiceClient, chainId: string, scHname: string, eventHandlers: EventHandlers) { + constructor(client: ServiceClient, chainId: string, scHname: client.Hname, eventHandlers: EventHandlers) { this.client = client; this.chainId = chainId; this.scHname = scHname; @@ -37,25 +53,23 @@ export class Service { } private handleIncomingMessage(message: MessageEvent): void { - const msg = message.data.toString().split('|'); - if (msg.length == 0) { - return; - } - const topics = msg[0].split(' '); - if (msg[0] != 'vmmsg') { + // expect vmmsg contract.event|parameters + const msg = message.data.toString().split(' '); + if (msg.length != 4 || msg[0] != 'vmmsg') { return; } - const topic = msg[3]; - if (typeof this.eventHandlers[topic] != 'undefined') { + const topics = msg[3].split('|'); + const topic = topics[0]; + if (this.eventHandlers[topic] != undefined) { this.eventHandlers[topic](msg.slice(1)); } } // calls a view - public async callView(viewName: string, args: client.Arguments): Promise { + public async callView(viewName: string, args: client.Arguments): Promise { const response = await this.client.callView( this.chainId, - this.scHname.toString(), + this.scHname.toString(16), viewName, args, ); @@ -72,7 +86,7 @@ export class Service { } // posts off-tangle request - public async postRequest(hFunc: client.Hname, args: client.Arguments): Promise { + public async postRequest(funcName: string, args: client.Arguments): Promise { let request: IOffLedger = { requestType: 1, noonce: BigInt(performance.now() + performance.timeOrigin * 10000000), diff --git a/packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json b/packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json index 80d11b5866..e86bc66ee4 100644 --- a/packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json +++ b/packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "commonjs", - "lib": "es2020", + "lib": ["es2020", "dom"], "target": "es2020", "sourceMap": true }, diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index 500797ec64..1b6bcd7a1a 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -16,13 +16,15 @@ $#each results constRes ) $#each func funcStruct +///////////////////////////// $PkgName$+Service ///////////////////////////// + type $PkgName$+Service struct { client.Service } -func New$PkgName$+Service(client client.ServiceClient, chainId string) *$PkgName$+Service { +func New$PkgName$+Service(cl client.ServiceClient, chainID string) *$PkgName$+Service { s := &$PkgName$+Service{} - s.Service.Init(client, chainId, "$hscName", EventHandlers) + s.Service.Init(cl, chainID, "$hscName", EventHandlers) return s } $#each func serviceFunction @@ -38,7 +40,10 @@ $#each func serviceFunction // ******************************* "funcStruct": ` +///////////////////////////// $funcName ///////////////////////////// + type $FuncName$Kind struct { + svc *client.Service $#if param funcArgsMember } $#each param funcArgSetter @@ -60,9 +65,9 @@ func (f $FuncName$Kind) $FldName(v $fldLangType) { func (f $FuncName$Kind) Post() { $#each param mandatoryCheck -$#set exec Post +$#set exec f.svc.PostRequest $#if param execWithArgs execNoArgs - //TODO Do$exec + $exec } `, // ******************************* @@ -70,10 +75,9 @@ $#if param execWithArgs execNoArgs func (f $FuncName$Kind) Call() $FuncName$+Results { $#each param mandatoryCheck -$#set exec Call +$#set exec f.svc.CallView $#if param execWithArgs execNoArgs - //TODO Do$exec instead of client.NewResults() - return $FuncName$+Results { res: client.NewResults() } + return $FuncName$+Results { res: $exec } } $#if result resultStruct `, @@ -83,11 +87,11 @@ $#if result resultStruct `, // ******************************* "execWithArgs": ` -$#set exec $exec(f.args) +$#set exec $exec("$funcName", &f.args) `, // ******************************* "execNoArgs": ` -$#set exec $exec(nil) +$#set exec $exec("$funcName", nil) `, // ******************************* "resultStruct": ` @@ -126,7 +130,7 @@ func (r $FuncName$+Results) $FldName$+Exists() bool { "serviceFunction": ` func (s *$PkgName$+Service) $FuncName() $FuncName$Kind { - return $FuncName$Kind{} + return $FuncName$Kind{ svc: &s.Service } } `, } diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index b52fa0e2ce..f88ef71d65 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -11,10 +11,12 @@ $#each params constArg $#each results constRes $#each func funcStruct +///////////////////////////// $PkgName$+Service ///////////////////////////// + export class $PkgName$+Service extends client.Service { - constructor(client: client.ServiceClient, chainId: string) { - super(client, chainId, "$hscName", events.eventHandlers); + constructor(cl: client.ServiceClient, chainID: string) { + super(cl, chainID, "$hscName", events.eventHandlers); } $#each func serviceFunction } @@ -30,7 +32,9 @@ const Res$FldName = "$fldAlias"; // ******************************* "funcStruct": ` -export class $FuncName$Kind { +///////////////////////////// $funcName ///////////////////////////// + +export class $FuncName$Kind extends client.FuncObject { $#if param funcArgsMember $#each param funcArgSetter $#if func funcPost viewCall @@ -51,22 +55,21 @@ $#if result resultStruct // ******************************* "funcPost": ` - post(): void { + public async post(): Promise { $#each param mandatoryCheck -$#set exec Post +$#set exec this.svc.postRequest $#if param execWithArgs execNoArgs - //TODO Do$exec + $exec; } `, // ******************************* "viewCall": ` - call(): $FuncName$+Results { + public async call(): Promise<$FuncName$+Results> { $#each param mandatoryCheck -$#set exec Call +$#set exec this.svc.callView $#if param execWithArgs execNoArgs - //TODO Do$exec instead of new client.Results() - return new $FuncName$+Results(new client.Results()); + return new $FuncName$+Results($exec); } `, // ******************************* @@ -75,19 +78,16 @@ $#if param execWithArgs execNoArgs `, // ******************************* "execWithArgs": ` -$#set exec $exec(this.args) +$#set exec $exec("$funcName", this.args) `, // ******************************* "execNoArgs": ` -$#set exec $exec(null) +$#set exec $exec("$funcName", null) `, // ******************************* "resultStruct": ` -export class $FuncName$+Results { - res: client.Results; - - constructor(res: client.Results) { this.res = res; } +export class $FuncName$+Results extends client.ViewResults { $#each result callResultGetter } `, @@ -121,7 +121,7 @@ $#if mandatory else callResultOptional "serviceFunction": ` public $funcName(): $FuncName$Kind { - return new $FuncName$Kind(); + return new $FuncName$Kind(this); } `, } From 654d9ac00019449ae13e71a9801a22c131e23c60 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 15 Dec 2021 16:11:37 -0800 Subject: [PATCH 011/120] Fixed wasp-cli offledger nonce always zero --- tools/wasp-cli/chain/postrequest.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/wasp-cli/chain/postrequest.go b/tools/wasp-cli/chain/postrequest.go index cae1562aed..1be3c30bfc 100644 --- a/tools/wasp-cli/chain/postrequest.go +++ b/tools/wasp-cli/chain/postrequest.go @@ -3,6 +3,7 @@ package chain import ( "strconv" "strings" + "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/client/chainclient" @@ -34,6 +35,7 @@ func postRequestCmd() *cobra.Command { scClient := SCClient(iscp.Hn(args[0])) if offLedger { + params.Nonce = uint64(time.Now().UnixNano()) util.WithOffLedgerRequest(GetCurrentChainID(), func() (*request.OffLedger, error) { return scClient.PostOffLedgerRequest(fname, params) }) From e5de5f7bfbcd2bbcc31f324e422dc05fb6f49313 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 16 Dec 2021 12:02:54 -0800 Subject: [PATCH 012/120] wasp-cli nonce fix --- client/chainclient/chainclient.go | 7 ++++--- tools/wasp-cli/chain/postrequest.go | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/client/chainclient/chainclient.go b/client/chainclient/chainclient.go index 28f17fdae2..e2d9e70c9b 100644 --- a/client/chainclient/chainclient.go +++ b/client/chainclient/chainclient.go @@ -1,8 +1,6 @@ package chainclient import ( - "time" - "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/client" @@ -23,6 +21,7 @@ type Client struct { WaspClient *client.WaspClient ChainID *iscp.ChainID KeyPair *ed25519.KeyPair + nonces map[ed25519.PublicKey]uint64 } // New creates a new chainclient.Client @@ -37,6 +36,7 @@ func New( WaspClient: waspClient, ChainID: chainID, KeyPair: keyPair, + nonces: make(map[ed25519.PublicKey]uint64), } } @@ -79,7 +79,8 @@ func (c *Client) PostOffLedgerRequest( par = params[0] } if par.Nonce == 0 { - par.Nonce = uint64(time.Now().UnixNano()) + c.nonces[c.KeyPair.PublicKey]++ + par.Nonce = c.nonces[c.KeyPair.PublicKey] } offledgerReq := request.NewOffLedger(c.ChainID, contractHname, entrypoint, par.Args).WithTransfer(par.Transfer) offledgerReq.WithNonce(par.Nonce) diff --git a/tools/wasp-cli/chain/postrequest.go b/tools/wasp-cli/chain/postrequest.go index cae1562aed..1be3c30bfc 100644 --- a/tools/wasp-cli/chain/postrequest.go +++ b/tools/wasp-cli/chain/postrequest.go @@ -3,6 +3,7 @@ package chain import ( "strconv" "strings" + "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/client/chainclient" @@ -34,6 +35,7 @@ func postRequestCmd() *cobra.Command { scClient := SCClient(iscp.Hn(args[0])) if offLedger { + params.Nonce = uint64(time.Now().UnixNano()) util.WithOffLedgerRequest(GetCurrentChainID(), func() (*request.OffLedger, error) { return scClient.PostOffLedgerRequest(fname, params) }) From 8c8c1b9618007c28facf288bbb50dc79562fedc5 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 16 Dec 2021 20:38:39 -0800 Subject: [PATCH 013/120] Updated to latest wasmtime and wasmedge --- go.mod | 4 ++-- go.sum | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 669a802d98..3033a7f605 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,9 @@ require ( github.com/PuerkitoBio/goquery v1.6.1 github.com/anthdm/hbbft v0.0.0-20190702061856-0826ffdcf567 github.com/bygui86/multi-profile/v2 v2.1.0 - github.com/bytecodealliance/wasmtime-go v0.31.0 + github.com/bytecodealliance/wasmtime-go v0.32.0 github.com/wasmerio/wasmer-go v1.0.4 - github.com/second-state/WasmEdge-go v0.9.0-rc3 // indirect + github.com/second-state/WasmEdge-go v0.9.0 // indirect github.com/ethereum/go-ethereum v1.10.10 github.com/iotaledger/goshimmer v0.7.5-0.20210811162925-25c827e8326a github.com/iotaledger/hive.go v0.0.0-20210625103722-68b2cf52ef4e diff --git a/go.sum b/go.sum index 6ba6d789cd..3f37349bad 100644 --- a/go.sum +++ b/go.sum @@ -155,6 +155,8 @@ github.com/bytecodealliance/wasmtime-go v0.21.0 h1:8C6fNmpfzF6QQ9h0PCvGaYORhtgMv github.com/bytecodealliance/wasmtime-go v0.21.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= github.com/bytecodealliance/wasmtime-go v0.31.0 h1:AbMdV1pwjw/0Ito5yARcGzY366cq5NIiDk5vpy1c2Lw= github.com/bytecodealliance/wasmtime-go v0.31.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= +github.com/bytecodealliance/wasmtime-go v0.32.0 h1:/GsrnJz2bfULAIZygN4vUElLYliQrx/o/1opP9X7Gck= +github.com/bytecodealliance/wasmtime-go v0.32.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/capossele/asset-registry v0.0.0-20210521112927-c9d6e74574e8/go.mod h1:BXwVCA0+rgYcMKC3vVkfjF+2nXYIYq3h/HndbaCuw08= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= @@ -1294,6 +1296,8 @@ github.com/second-state/WasmEdge-go v0.8.2 h1:dD5+ZKY9Vr+Ye5F9VZGP/WP2MnXfORQocU github.com/second-state/WasmEdge-go v0.8.2/go.mod h1:Q3h79fwQs7GUSOm3ZzHXK+j4cKmCFP3SKpGfFNDtqD8= github.com/second-state/WasmEdge-go v0.9.0-rc3 h1:Ol6KaPguuqyd42vb9W7VCRR4V92yZdVpqUh0MWD07Y4= github.com/second-state/WasmEdge-go v0.9.0-rc3/go.mod h1:Q3h79fwQs7GUSOm3ZzHXK+j4cKmCFP3SKpGfFNDtqD8= +github.com/second-state/WasmEdge-go v0.9.0 h1:CapdsNNP5HG8nIeX8qmRteie8SZC/+BkmNXq+IoQlKE= +github.com/second-state/WasmEdge-go v0.9.0/go.mod h1:Q3h79fwQs7GUSOm3ZzHXK+j4cKmCFP3SKpGfFNDtqD8= 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/sercand/kuberesolver v2.1.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ= From 56c0d614e9e57342d0034edf48d62ab56dfbf6f0 Mon Sep 17 00:00:00 2001 From: Dr-Electron Date: Sat, 18 Dec 2021 00:03:05 +0100 Subject: [PATCH 014/120] Add pull request template --- .github/pull_request_template.md | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..6dfc370f71 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,34 @@ +# Description of change + +Please write a summary of your changes and why you made them. + +## Links to any relevant issues + +Be sure to reference any related issues by adding `fixes issue #`. + +## Type of change + +Choose a type of change, and delete any options that are not relevant. + +- Bug fix (a non-breaking change which fixes an issue) +- Enhancement (a non-breaking change which adds functionality) +- Breaking change (fix or feature that would cause existing functionality to not work as expected) +- Documentation Fix + +## How the change has been tested + +Describe the tests that you ran to verify your changes. + +Make sure to provide instructions for the maintainer as well as any relevant configurations. + +## Change checklist + +Tick the boxes that are relevant to your changes, and delete any items that are not. + +- [ ] I have followed the [contribution guidelines](https://wiki.iota.org/smart-contracts/contribute) for this project +- [ ] I have performed a self-review of my own code +- [ ] I have selected the `develop` branch as the target branch +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] I have checked that new and existing unit tests pass locally with my changes From fcfa184196d9152d47f6e026f6ab45e8e2133ddb Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 22 Dec 2021 15:28:20 -0800 Subject: [PATCH 015/120] Small fixes to schema tool --- contracts/wasm/erc721/client/go/erc721.go | 19 - contracts/wasm/erc721/client/go/events.go | 98 ---- contracts/wasm/erc721/client/go/service.go | 434 ------------------ contracts/wasm/erc721/client/ts/erc721.ts | 23 - contracts/wasm/erc721/client/ts/events.ts | 85 ---- contracts/wasm/erc721/client/ts/index.ts | 6 - contracts/wasm/erc721/client/ts/service.ts | 405 ---------------- contracts/wasm/erc721/client/ts/tsconfig.json | 11 - .../wasm/fairroulette/client/go/events.go | 95 ---- .../fairroulette/client/go/fairroulette.go | 22 - .../wasm/fairroulette/client/go/service.go | 172 ------- .../wasm/fairroulette/client/ts/events.ts | 80 ---- .../fairroulette/client/ts/fairroulette.ts | 26 -- .../wasm/fairroulette/client/ts/service.ts | 179 -------- .../wasm/fairroulette/client/ts/tsconfig.json | 11 - tools/schema/generator/emitter.go | 3 +- tools/schema/generator/generator_tsclient.go | 19 + .../goclienttemplates/alltemplates.go | 11 +- .../generator/goclienttemplates/service.go | 4 +- .../generator/gotemplates/alltemplates.go | 2 +- tools/schema/generator/gotemplates/proxy.go | 2 +- .../generator/rstemplates/alltemplates.go | 2 +- tools/schema/generator/rstemplates/proxy.go | 2 +- .../tsclienttemplates/alltemplates.go | 20 +- .../generator/tsclienttemplates/index.go | 10 + .../generator/tsclienttemplates/service.go | 4 +- .../generator/tstemplates/alltemplates.go | 2 +- 27 files changed, 58 insertions(+), 1689 deletions(-) delete mode 100644 contracts/wasm/erc721/client/go/erc721.go delete mode 100644 contracts/wasm/erc721/client/go/events.go delete mode 100644 contracts/wasm/erc721/client/go/service.go delete mode 100644 contracts/wasm/erc721/client/ts/erc721.ts delete mode 100644 contracts/wasm/erc721/client/ts/events.ts delete mode 100644 contracts/wasm/erc721/client/ts/index.ts delete mode 100644 contracts/wasm/erc721/client/ts/service.ts delete mode 100644 contracts/wasm/erc721/client/ts/tsconfig.json delete mode 100644 contracts/wasm/fairroulette/client/go/events.go delete mode 100644 contracts/wasm/fairroulette/client/go/fairroulette.go delete mode 100644 contracts/wasm/fairroulette/client/go/service.go delete mode 100644 contracts/wasm/fairroulette/client/ts/events.ts delete mode 100644 contracts/wasm/fairroulette/client/ts/fairroulette.ts delete mode 100644 contracts/wasm/fairroulette/client/ts/service.ts delete mode 100644 contracts/wasm/fairroulette/client/ts/tsconfig.json create mode 100644 tools/schema/generator/tsclienttemplates/index.go diff --git a/contracts/wasm/erc721/client/go/erc721.go b/contracts/wasm/erc721/client/go/erc721.go deleted file mode 100644 index e5f50dbac3..0000000000 --- a/contracts/wasm/erc721/client/go/erc721.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package erc721 - -func OnErc721Approval(event *EventApproval) { -} - -func OnErc721ApprovalForAll(event *EventApprovalForAll) { -} - -func OnErc721Init(event *EventInit) { -} - -func OnErc721Mint(event *EventMint) { -} - -func OnErc721Transfer(event *EventTransfer) { -} diff --git a/contracts/wasm/erc721/client/go/events.go b/contracts/wasm/erc721/client/go/events.go deleted file mode 100644 index c1cb557abd..0000000000 --- a/contracts/wasm/erc721/client/go/events.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -package erc721 - -import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" -) - -var EventHandlers = map[string]func([]string){ - "erc721.approval": onErc721ApprovalThunk, - "erc721.approvalForAll": onErc721ApprovalForAllThunk, - "erc721.init": onErc721InitThunk, - "erc721.mint": onErc721MintThunk, - "erc721.transfer": onErc721TransferThunk, -} - -type EventApproval struct { - client.Event - Approved client.AgentID - Owner client.AgentID - TokenID client.Hash -} - -func onErc721ApprovalThunk(message []string) { - e := &EventApproval{} - e.Init(message) - e.Approved = e.NextAgentID() - e.Owner = e.NextAgentID() - e.TokenID = e.NextHash() - OnErc721Approval(e) -} - -type EventApprovalForAll struct { - client.Event - Approval bool - Operator client.AgentID - Owner client.AgentID -} - -func onErc721ApprovalForAllThunk(message []string) { - e := &EventApprovalForAll{} - e.Init(message) - e.Approval = e.NextBool() - e.Operator = e.NextAgentID() - e.Owner = e.NextAgentID() - OnErc721ApprovalForAll(e) -} - -type EventInit struct { - client.Event - Name string - Symbol string -} - -func onErc721InitThunk(message []string) { - e := &EventInit{} - e.Init(message) - e.Name = e.NextString() - e.Symbol = e.NextString() - OnErc721Init(e) -} - -type EventMint struct { - client.Event - Balance uint64 - Owner client.AgentID - TokenID client.Hash -} - -func onErc721MintThunk(message []string) { - e := &EventMint{} - e.Init(message) - e.Balance = e.NextUint64() - e.Owner = e.NextAgentID() - e.TokenID = e.NextHash() - OnErc721Mint(e) -} - -type EventTransfer struct { - client.Event - From client.AgentID - To client.AgentID - TokenID client.Hash -} - -func onErc721TransferThunk(message []string) { - e := &EventTransfer{} - e.Init(message) - e.From = e.NextAgentID() - e.To = e.NextAgentID() - e.TokenID = e.NextHash() - OnErc721Transfer(e) -} diff --git a/contracts/wasm/erc721/client/go/service.go b/contracts/wasm/erc721/client/go/service.go deleted file mode 100644 index b3b4f0241a..0000000000 --- a/contracts/wasm/erc721/client/go/service.go +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -package erc721 - -import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" -) - -const ( - ArgApproval = "approval" - ArgApproved = "approved" - ArgData = "data" - ArgFrom = "from" - ArgName = "n" - ArgOperator = "operator" - ArgOwner = "owner" - ArgSymbol = "s" - ArgTo = "to" - ArgTokenID = "tokenID" - - ResAmount = "amount" - ResApproval = "approval" - ResApproved = "approved" - ResName = "name" - ResOwner = "owner" - ResSymbol = "symbol" - ResTokenURI = "tokenURI" -) - -///////////////////////////// approve ///////////////////////////// - -type ApproveFunc struct { - svc *client.Service - args client.Arguments -} - -func (f ApproveFunc) Approved(v client.AgentID) { - f.args.SetAgentID(ArgApproved, v) -} - -func (f ApproveFunc) TokenID(v client.Hash) { - f.args.SetHash(ArgTokenID, v) -} - -func (f ApproveFunc) Post() { - f.args.Mandatory(ArgApproved) - f.args.Mandatory(ArgTokenID) - f.svc.PostRequest("approve", &f.args) -} - -///////////////////////////// burn ///////////////////////////// - -type BurnFunc struct { - svc *client.Service - args client.Arguments -} - -func (f BurnFunc) TokenID(v client.Hash) { - f.args.SetHash(ArgTokenID, v) -} - -func (f BurnFunc) Post() { - f.args.Mandatory(ArgTokenID) - f.svc.PostRequest("burn", &f.args) -} - -///////////////////////////// init ///////////////////////////// - -type InitFunc struct { - svc *client.Service - args client.Arguments -} - -func (f InitFunc) Name(v string) { - f.args.SetString(ArgName, v) -} - -func (f InitFunc) Symbol(v string) { - f.args.SetString(ArgSymbol, v) -} - -func (f InitFunc) Post() { - f.args.Mandatory(ArgName) - f.args.Mandatory(ArgSymbol) - f.svc.PostRequest("init", &f.args) -} - -///////////////////////////// mint ///////////////////////////// - -type MintFunc struct { - svc *client.Service - args client.Arguments -} - -func (f MintFunc) TokenID(v client.Hash) { - f.args.SetHash(ArgTokenID, v) -} - -func (f MintFunc) Post() { - f.args.Mandatory(ArgTokenID) - f.svc.PostRequest("mint", &f.args) -} - -///////////////////////////// safeTransferFrom ///////////////////////////// - -type SafeTransferFromFunc struct { - svc *client.Service - args client.Arguments -} - -func (f SafeTransferFromFunc) Data(v []byte) { - f.args.SetBytes(ArgData, v) -} - -func (f SafeTransferFromFunc) From(v client.AgentID) { - f.args.SetAgentID(ArgFrom, v) -} - -func (f SafeTransferFromFunc) To(v client.AgentID) { - f.args.SetAgentID(ArgTo, v) -} - -func (f SafeTransferFromFunc) TokenID(v client.Hash) { - f.args.SetHash(ArgTokenID, v) -} - -func (f SafeTransferFromFunc) Post() { - f.args.Mandatory(ArgData) - f.args.Mandatory(ArgFrom) - f.args.Mandatory(ArgTo) - f.args.Mandatory(ArgTokenID) - f.svc.PostRequest("safeTransferFrom", &f.args) -} - -///////////////////////////// setApprovalForAll ///////////////////////////// - -type SetApprovalForAllFunc struct { - svc *client.Service - args client.Arguments -} - -func (f SetApprovalForAllFunc) Approval(v bool) { - f.args.SetBool(ArgApproval, v) -} - -func (f SetApprovalForAllFunc) Operator(v client.AgentID) { - f.args.SetAgentID(ArgOperator, v) -} - -func (f SetApprovalForAllFunc) Post() { - f.args.Mandatory(ArgApproval) - f.args.Mandatory(ArgOperator) - f.svc.PostRequest("setApprovalForAll", &f.args) -} - -///////////////////////////// transferFrom ///////////////////////////// - -type TransferFromFunc struct { - svc *client.Service - args client.Arguments -} - -func (f TransferFromFunc) From(v client.AgentID) { - f.args.SetAgentID(ArgFrom, v) -} - -func (f TransferFromFunc) To(v client.AgentID) { - f.args.SetAgentID(ArgTo, v) -} - -func (f TransferFromFunc) TokenID(v client.Hash) { - f.args.SetHash(ArgTokenID, v) -} - -func (f TransferFromFunc) Post() { - f.args.Mandatory(ArgFrom) - f.args.Mandatory(ArgTo) - f.args.Mandatory(ArgTokenID) - f.svc.PostRequest("transferFrom", &f.args) -} - -///////////////////////////// balanceOf ///////////////////////////// - -type BalanceOfView struct { - svc *client.Service - args client.Arguments -} - -func (f BalanceOfView) Owner(v client.AgentID) { - f.args.SetAgentID(ArgOwner, v) -} - -func (f BalanceOfView) Call() BalanceOfResults { - f.args.Mandatory(ArgOwner) - return BalanceOfResults{res: f.svc.CallView("balanceOf", &f.args)} -} - -type BalanceOfResults struct { - res client.Results -} - -func (r BalanceOfResults) AmountExists() bool { - return r.res.Exists(ResAmount) -} - -func (r BalanceOfResults) Amount() uint64 { - return r.res.GetUint64(ResAmount) -} - -///////////////////////////// getApproved ///////////////////////////// - -type GetApprovedView struct { - svc *client.Service - args client.Arguments -} - -func (f GetApprovedView) TokenID(v client.Hash) { - f.args.SetHash(ArgTokenID, v) -} - -func (f GetApprovedView) Call() GetApprovedResults { - f.args.Mandatory(ArgTokenID) - return GetApprovedResults{res: f.svc.CallView("getApproved", &f.args)} -} - -type GetApprovedResults struct { - res client.Results -} - -func (r GetApprovedResults) ApprovedExists() bool { - return r.res.Exists(ResApproved) -} - -func (r GetApprovedResults) Approved() client.AgentID { - return r.res.GetAgentID(ResApproved) -} - -///////////////////////////// isApprovedForAll ///////////////////////////// - -type IsApprovedForAllView struct { - svc *client.Service - args client.Arguments -} - -func (f IsApprovedForAllView) Operator(v client.AgentID) { - f.args.SetAgentID(ArgOperator, v) -} - -func (f IsApprovedForAllView) Owner(v client.AgentID) { - f.args.SetAgentID(ArgOwner, v) -} - -func (f IsApprovedForAllView) Call() IsApprovedForAllResults { - f.args.Mandatory(ArgOperator) - f.args.Mandatory(ArgOwner) - return IsApprovedForAllResults{res: f.svc.CallView("isApprovedForAll", &f.args)} -} - -type IsApprovedForAllResults struct { - res client.Results -} - -func (r IsApprovedForAllResults) ApprovalExists() bool { - return r.res.Exists(ResApproval) -} - -func (r IsApprovedForAllResults) Approval() bool { - return r.res.GetBool(ResApproval) -} - -///////////////////////////// name ///////////////////////////// - -type NameView struct { - svc *client.Service -} - -func (f NameView) Call() NameResults { - return NameResults{res: f.svc.CallView("name", nil)} -} - -type NameResults struct { - res client.Results -} - -func (r NameResults) Name() string { - return r.res.GetString(ResName) -} - -///////////////////////////// ownerOf ///////////////////////////// - -type OwnerOfView struct { - svc *client.Service - args client.Arguments -} - -func (f OwnerOfView) TokenID(v client.Hash) { - f.args.SetHash(ArgTokenID, v) -} - -func (f OwnerOfView) Call() OwnerOfResults { - f.args.Mandatory(ArgTokenID) - return OwnerOfResults{res: f.svc.CallView("ownerOf", &f.args)} -} - -type OwnerOfResults struct { - res client.Results -} - -func (r OwnerOfResults) OwnerExists() bool { - return r.res.Exists(ResOwner) -} - -func (r OwnerOfResults) Owner() client.AgentID { - return r.res.GetAgentID(ResOwner) -} - -///////////////////////////// symbol ///////////////////////////// - -type SymbolView struct { - svc *client.Service -} - -func (f SymbolView) Call() SymbolResults { - return SymbolResults{res: f.svc.CallView("symbol", nil)} -} - -type SymbolResults struct { - res client.Results -} - -func (r SymbolResults) Symbol() string { - return r.res.GetString(ResSymbol) -} - -///////////////////////////// tokenURI ///////////////////////////// - -type TokenURIView struct { - svc *client.Service - args client.Arguments -} - -func (f TokenURIView) TokenID(v client.Hash) { - f.args.SetHash(ArgTokenID, v) -} - -func (f TokenURIView) Call() TokenURIResults { - f.args.Mandatory(ArgTokenID) - return TokenURIResults{res: f.svc.CallView("tokenURI", &f.args)} -} - -type TokenURIResults struct { - res client.Results -} - -func (r TokenURIResults) TokenURIExists() bool { - return r.res.Exists(ResTokenURI) -} - -func (r TokenURIResults) TokenURI() string { - return r.res.GetString(ResTokenURI) -} - -///////////////////////////// Erc721Service ///////////////////////////// - -type Erc721Service struct { - client.Service -} - -func NewErc721Service(cl client.ServiceClient, chainID string) *Erc721Service { - s := &Erc721Service{} - s.Service.Init(cl, chainID, "d967c216", EventHandlers) - return s -} - -func (s *Erc721Service) Approve() ApproveFunc { - return ApproveFunc{svc: &s.Service} -} - -func (s *Erc721Service) Burn() BurnFunc { - return BurnFunc{svc: &s.Service} -} - -func (s *Erc721Service) Init() InitFunc { - return InitFunc{svc: &s.Service} -} - -func (s *Erc721Service) Mint() MintFunc { - return MintFunc{svc: &s.Service} -} - -func (s *Erc721Service) SafeTransferFrom() SafeTransferFromFunc { - return SafeTransferFromFunc{svc: &s.Service} -} - -func (s *Erc721Service) SetApprovalForAll() SetApprovalForAllFunc { - return SetApprovalForAllFunc{svc: &s.Service} -} - -func (s *Erc721Service) TransferFrom() TransferFromFunc { - return TransferFromFunc{svc: &s.Service} -} - -func (s *Erc721Service) BalanceOf() BalanceOfView { - return BalanceOfView{svc: &s.Service} -} - -func (s *Erc721Service) GetApproved() GetApprovedView { - return GetApprovedView{svc: &s.Service} -} - -func (s *Erc721Service) IsApprovedForAll() IsApprovedForAllView { - return IsApprovedForAllView{svc: &s.Service} -} - -func (s *Erc721Service) Name() NameView { - return NameView{svc: &s.Service} -} - -func (s *Erc721Service) OwnerOf() OwnerOfView { - return OwnerOfView{svc: &s.Service} -} - -func (s *Erc721Service) Symbol() SymbolView { - return SymbolView{svc: &s.Service} -} - -func (s *Erc721Service) TokenURI() TokenURIView { - return TokenURIView{svc: &s.Service} -} diff --git a/contracts/wasm/erc721/client/ts/erc721.ts b/contracts/wasm/erc721/client/ts/erc721.ts deleted file mode 100644 index 2c0b480a78..0000000000 --- a/contracts/wasm/erc721/client/ts/erc721.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import * as events from "./events" -import * as service from "./service" - -const client = new BasicClient(config); -const erc721Service = new service.Erc721Service(client, config.ChainId); - -export function onErc721Approval(event: events.EventApproval): void { -} - -export function onErc721ApprovalForAll(event: events.EventApprovalForAll): void { -} - -export function onErc721Init(event: events.EventInit): void { -} - -export function onErc721Mint(event: events.EventMint): void { -} - -export function onErc721Transfer(event: events.EventTransfer): void { -} diff --git a/contracts/wasm/erc721/client/ts/events.ts b/contracts/wasm/erc721/client/ts/events.ts deleted file mode 100644 index f3abe2bd1e..0000000000 --- a/contracts/wasm/erc721/client/ts/events.ts +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -import * as client from "wasmlib/client" -import * as app from "./erc721" - -export const eventHandlers: client.EventHandlers = { - "erc721.approval": onErc721ApprovalThunk, - "erc721.approvalForAll": onErc721ApprovalForAllThunk, - "erc721.init": onErc721InitThunk, - "erc721.mint": onErc721MintThunk, - "erc721.transfer": onErc721TransferThunk, -}; - -export class EventApproval extends client.Event { - public approved: client.AgentID; - public owner: client.AgentID; - public tokenID: client.Hash; -} - -function onErc721ApprovalThunk(message: string[]) { - let e = new EventApproval(message); - e.approved = e.nextAgentID(); - e.owner = e.nextAgentID(); - e.tokenID = e.nextHash(); - app.onErc721Approval(e); -} - -export class EventApprovalForAll extends client.Event { - public approval: client.Bool; - public operator: client.AgentID; - public owner: client.AgentID; -} - -function onErc721ApprovalForAllThunk(message: string[]) { - let e = new EventApprovalForAll(message); - e.approval = e.nextBool(); - e.operator = e.nextAgentID(); - e.owner = e.nextAgentID(); - app.onErc721ApprovalForAll(e); -} - -export class EventInit extends client.Event { - public name: client.String; - public symbol: client.String; -} - -function onErc721InitThunk(message: string[]) { - let e = new EventInit(message); - e.name = e.nextString(); - e.symbol = e.nextString(); - app.onErc721Init(e); -} - -export class EventMint extends client.Event { - public balance: client.Uint64; - public owner: client.AgentID; - public tokenID: client.Hash; -} - -function onErc721MintThunk(message: string[]) { - let e = new EventMint(message); - e.balance = e.nextUint64(); - e.owner = e.nextAgentID(); - e.tokenID = e.nextHash(); - app.onErc721Mint(e); -} - -export class EventTransfer extends client.Event { - public from: client.AgentID; - public to: client.AgentID; - public tokenID: client.Hash; -} - -function onErc721TransferThunk(message: string[]) { - let e = new EventTransfer(message); - e.from = e.nextAgentID(); - e.to = e.nextAgentID(); - e.tokenID = e.nextHash(); - app.onErc721Transfer(e); -} diff --git a/contracts/wasm/erc721/client/ts/index.ts b/contracts/wasm/erc721/client/ts/index.ts deleted file mode 100644 index 84d225f2ab..0000000000 --- a/contracts/wasm/erc721/client/ts/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -export * from "./erc721"; -export * from "./events"; -export * from "./service"; diff --git a/contracts/wasm/erc721/client/ts/service.ts b/contracts/wasm/erc721/client/ts/service.ts deleted file mode 100644 index 39095541a1..0000000000 --- a/contracts/wasm/erc721/client/ts/service.ts +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -import * as client from "wasmlib/client" -import * as events from "./events" - -const ArgApproval = "approval"; -const ArgApproved = "approved"; -const ArgData = "data"; -const ArgFrom = "from"; -const ArgName = "n"; -const ArgOperator = "operator"; -const ArgOwner = "owner"; -const ArgSymbol = "s"; -const ArgTo = "to"; -const ArgTokenID = "tokenID"; - -const ResAmount = "amount"; -const ResApproval = "approval"; -const ResApproved = "approved"; -const ResName = "name"; -const ResOwner = "owner"; -const ResSymbol = "symbol"; -const ResTokenURI = "tokenURI"; - -///////////////////////////// approve ///////////////////////////// - -export class ApproveFunc extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - approved(v: client.AgentID): void { - this.args.setAgentID(ArgApproved, v); - } - - tokenID(v: client.Hash): void { - this.args.setHash(ArgTokenID, v); - } - - public async post(): Promise { - this.args.mandatory(ArgApproved); - this.args.mandatory(ArgTokenID); - this.svc.postRequest("approve", this.args); - } -} - -///////////////////////////// burn ///////////////////////////// - -export class BurnFunc extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - tokenID(v: client.Hash): void { - this.args.setHash(ArgTokenID, v); - } - - public async post(): Promise { - this.args.mandatory(ArgTokenID); - this.svc.postRequest("burn", this.args); - } -} - -///////////////////////////// init ///////////////////////////// - -export class InitFunc extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - name(v: string): void { - this.args.setString(ArgName, v); - } - - symbol(v: string): void { - this.args.setString(ArgSymbol, v); - } - - public async post(): Promise { - this.args.mandatory(ArgName); - this.args.mandatory(ArgSymbol); - this.svc.postRequest("init", this.args); - } -} - -///////////////////////////// mint ///////////////////////////// - -export class MintFunc extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - tokenID(v: client.Hash): void { - this.args.setHash(ArgTokenID, v); - } - - public async post(): Promise { - this.args.mandatory(ArgTokenID); - this.svc.postRequest("mint", this.args); - } -} - -///////////////////////////// safeTransferFrom ///////////////////////////// - -export class SafeTransferFromFunc extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - data(v: client.Bytes): void { - this.args.setBytes(ArgData, v); - } - - from(v: client.AgentID): void { - this.args.setAgentID(ArgFrom, v); - } - - to(v: client.AgentID): void { - this.args.setAgentID(ArgTo, v); - } - - tokenID(v: client.Hash): void { - this.args.setHash(ArgTokenID, v); - } - - public async post(): Promise { - this.args.mandatory(ArgData); - this.args.mandatory(ArgFrom); - this.args.mandatory(ArgTo); - this.args.mandatory(ArgTokenID); - this.svc.postRequest("safeTransferFrom", this.args); - } -} - -///////////////////////////// setApprovalForAll ///////////////////////////// - -export class SetApprovalForAllFunc extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - approval(v: boolean): void { - this.args.setBool(ArgApproval, v); - } - - operator(v: client.AgentID): void { - this.args.setAgentID(ArgOperator, v); - } - - public async post(): Promise { - this.args.mandatory(ArgApproval); - this.args.mandatory(ArgOperator); - this.svc.postRequest("setApprovalForAll", this.args); - } -} - -///////////////////////////// transferFrom ///////////////////////////// - -export class TransferFromFunc extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - from(v: client.AgentID): void { - this.args.setAgentID(ArgFrom, v); - } - - to(v: client.AgentID): void { - this.args.setAgentID(ArgTo, v); - } - - tokenID(v: client.Hash): void { - this.args.setHash(ArgTokenID, v); - } - - public async post(): Promise { - this.args.mandatory(ArgFrom); - this.args.mandatory(ArgTo); - this.args.mandatory(ArgTokenID); - this.svc.postRequest("transferFrom", this.args); - } -} - -///////////////////////////// balanceOf ///////////////////////////// - -export class BalanceOfView extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - owner(v: client.AgentID): void { - this.args.setAgentID(ArgOwner, v); - } - - public async call(): Promise { - this.args.mandatory(ArgOwner); - return new BalanceOfResults(this.svc.callView("balanceOf", this.args)); - } -} - -export class BalanceOfResults extends client.ViewResults { - - amountExists(): boolean { - return this.res.exists(ResAmount) - } - - amount(): client.Uint64 { - return this.res.getUint64(ResAmount); - } -} - -///////////////////////////// getApproved ///////////////////////////// - -export class GetApprovedView extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - tokenID(v: client.Hash): void { - this.args.setHash(ArgTokenID, v); - } - - public async call(): Promise { - this.args.mandatory(ArgTokenID); - return new GetApprovedResults(this.svc.callView("getApproved", this.args)); - } -} - -export class GetApprovedResults extends client.ViewResults { - - approvedExists(): boolean { - return this.res.exists(ResApproved) - } - - approved(): client.AgentID { - return this.res.getAgentID(ResApproved); - } -} - -///////////////////////////// isApprovedForAll ///////////////////////////// - -export class IsApprovedForAllView extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - operator(v: client.AgentID): void { - this.args.setAgentID(ArgOperator, v); - } - - owner(v: client.AgentID): void { - this.args.setAgentID(ArgOwner, v); - } - - public async call(): Promise { - this.args.mandatory(ArgOperator); - this.args.mandatory(ArgOwner); - return new IsApprovedForAllResults(this.svc.callView("isApprovedForAll", this.args)); - } -} - -export class IsApprovedForAllResults extends client.ViewResults { - - approvalExists(): boolean { - return this.res.exists(ResApproval) - } - - approval(): boolean { - return this.res.getBool(ResApproval); - } -} - -///////////////////////////// name ///////////////////////////// - -export class NameView extends client.FuncObject { - - public async call(): Promise { - return new NameResults(this.svc.callView("name", null)); - } -} - -export class NameResults extends client.ViewResults { - - name(): string { - return this.res.getString(ResName); - } -} - -///////////////////////////// ownerOf ///////////////////////////// - -export class OwnerOfView extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - tokenID(v: client.Hash): void { - this.args.setHash(ArgTokenID, v); - } - - public async call(): Promise { - this.args.mandatory(ArgTokenID); - return new OwnerOfResults(this.svc.callView("ownerOf", this.args)); - } -} - -export class OwnerOfResults extends client.ViewResults { - - ownerExists(): boolean { - return this.res.exists(ResOwner) - } - - owner(): client.AgentID { - return this.res.getAgentID(ResOwner); - } -} - -///////////////////////////// symbol ///////////////////////////// - -export class SymbolView extends client.FuncObject { - - public async call(): Promise { - return new SymbolResults(this.svc.callView("symbol", null)); - } -} - -export class SymbolResults extends client.ViewResults { - - symbol(): string { - return this.res.getString(ResSymbol); - } -} - -///////////////////////////// tokenURI ///////////////////////////// - -export class TokenURIView extends client.FuncObject { - args: client.Arguments = new client.Arguments(); - - tokenID(v: client.Hash): void { - this.args.setHash(ArgTokenID, v); - } - - public async call(): Promise { - this.args.mandatory(ArgTokenID); - return new TokenURIResults(this.svc.callView("tokenURI", this.args)); - } -} - -export class TokenURIResults extends client.ViewResults { - - tokenURIExists(): boolean { - return this.res.exists(ResTokenURI) - } - - tokenURI(): string { - return this.res.getString(ResTokenURI); - } -} - -///////////////////////////// Erc721Service ///////////////////////////// - -export class Erc721Service extends client.Service { - - constructor(cl: client.ServiceClient, chainID: string) { - super(cl, chainID, "d967c216", events.eventHandlers); - } - - public approve(): ApproveFunc { - return new ApproveFunc(this); - } - - public burn(): BurnFunc { - return new BurnFunc(this); - } - - public init(): InitFunc { - return new InitFunc(this); - } - - public mint(): MintFunc { - return new MintFunc(this); - } - - public safeTransferFrom(): SafeTransferFromFunc { - return new SafeTransferFromFunc(this); - } - - public setApprovalForAll(): SetApprovalForAllFunc { - return new SetApprovalForAllFunc(this); - } - - public transferFrom(): TransferFromFunc { - return new TransferFromFunc(this); - } - - public balanceOf(): BalanceOfView { - return new BalanceOfView(this); - } - - public getApproved(): GetApprovedView { - return new GetApprovedView(this); - } - - public isApprovedForAll(): IsApprovedForAllView { - return new IsApprovedForAllView(this); - } - - public name(): NameView { - return new NameView(this); - } - - public ownerOf(): OwnerOfView { - return new OwnerOfView(this); - } - - public symbol(): SymbolView { - return new SymbolView(this); - } - - public tokenURI(): TokenURIView { - return new TokenURIView(this); - } -} diff --git a/contracts/wasm/erc721/client/ts/tsconfig.json b/contracts/wasm/erc721/client/ts/tsconfig.json deleted file mode 100644 index b5353d085d..0000000000 --- a/contracts/wasm/erc721/client/ts/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "lib": ["es2020"], - "target": "es2020", - "sourceMap": true - }, - "exclude": [ - "node_modules" - ], -} \ No newline at end of file diff --git a/contracts/wasm/fairroulette/client/go/events.go b/contracts/wasm/fairroulette/client/go/events.go deleted file mode 100644 index cdf2c0b077..0000000000 --- a/contracts/wasm/fairroulette/client/go/events.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -package fairroulette - -import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" -) - -var EventHandlers = map[string]func([]string){ - "fairroulette.bet": onFairRouletteBetThunk, - "fairroulette.payout": onFairRoulettePayoutThunk, - "fairroulette.round": onFairRouletteRoundThunk, - "fairroulette.start": onFairRouletteStartThunk, - "fairroulette.stop": onFairRouletteStopThunk, - "fairroulette.winner": onFairRouletteWinnerThunk, -} - -type EventBet struct { - client.Event - Address client.Address - Amount int64 - Number int64 -} - -func onFairRouletteBetThunk(message []string) { - e := &EventBet{} - e.Init(message) - e.Address = e.NextAddress() - e.Amount = e.NextInt64() - e.Number = e.NextInt64() - OnFairRouletteBet(e) -} - -type EventPayout struct { - client.Event - Address client.Address - Amount int64 -} - -func onFairRoulettePayoutThunk(message []string) { - e := &EventPayout{} - e.Init(message) - e.Address = e.NextAddress() - e.Amount = e.NextInt64() - OnFairRoulettePayout(e) -} - -type EventRound struct { - client.Event - Number int64 -} - -func onFairRouletteRoundThunk(message []string) { - e := &EventRound{} - e.Init(message) - e.Number = e.NextInt64() - OnFairRouletteRound(e) -} - -type EventStart struct { - client.Event -} - -func onFairRouletteStartThunk(message []string) { - e := &EventStart{} - e.Init(message) - OnFairRouletteStart(e) -} - -type EventStop struct { - client.Event -} - -func onFairRouletteStopThunk(message []string) { - e := &EventStop{} - e.Init(message) - OnFairRouletteStop(e) -} - -type EventWinner struct { - client.Event - Number int64 -} - -func onFairRouletteWinnerThunk(message []string) { - e := &EventWinner{} - e.Init(message) - e.Number = e.NextInt64() - OnFairRouletteWinner(e) -} diff --git a/contracts/wasm/fairroulette/client/go/fairroulette.go b/contracts/wasm/fairroulette/client/go/fairroulette.go deleted file mode 100644 index 0f3e9ce2f6..0000000000 --- a/contracts/wasm/fairroulette/client/go/fairroulette.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package fairroulette - -func OnFairRouletteBet(event *EventBet) { -} - -func OnFairRoulettePayout(event *EventPayout) { -} - -func OnFairRouletteRound(event *EventRound) { -} - -func OnFairRouletteStart(event *EventStart) { -} - -func OnFairRouletteStop(event *EventStop) { -} - -func OnFairRouletteWinner(event *EventWinner) { -} diff --git a/contracts/wasm/fairroulette/client/go/service.go b/contracts/wasm/fairroulette/client/go/service.go deleted file mode 100644 index 13053b6620..0000000000 --- a/contracts/wasm/fairroulette/client/go/service.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -package fairroulette - -import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" -) - -const ( - ArgNumber = "number" - ArgPlayPeriod = "playPeriod" - - ResLastWinningNumber = "lastWinningNumber" - ResRoundNumber = "roundNumber" - ResRoundStartedAt = "roundStartedAt" - ResRoundStatus = "roundStatus" -) - -type ForcePayoutFunc struct{} - -func (f ForcePayoutFunc) Post() { - // TODO DoPost(nil) -} - -type ForceResetFunc struct{} - -func (f ForceResetFunc) Post() { - // TODO DoPost(nil) -} - -type PayWinnersFunc struct{} - -func (f PayWinnersFunc) Post() { - // TODO DoPost(nil) -} - -type PlaceBetFunc struct { - args client.Arguments -} - -func (f PlaceBetFunc) Number(v int64) { - f.args.SetInt64(ArgNumber, v) -} - -func (f PlaceBetFunc) Post() { - f.args.Mandatory(ArgNumber) - // TODO DoPost(f.args) -} - -type PlayPeriodFunc struct { - args client.Arguments -} - -func (f PlayPeriodFunc) PlayPeriod(v int32) { - f.args.SetInt32(ArgPlayPeriod, v) -} - -func (f PlayPeriodFunc) Post() { - f.args.Mandatory(ArgPlayPeriod) - // TODO DoPost(f.args) -} - -type LastWinningNumberView struct{} - -func (f LastWinningNumberView) Call() LastWinningNumberResults { - // TODO DoCall(nil) instead of client.NewResults() - return LastWinningNumberResults{res: client.NewResults()} -} - -type LastWinningNumberResults struct { - res client.Results -} - -func (r LastWinningNumberResults) LastWinningNumber() int64 { - return r.res.GetInt64(ResLastWinningNumber) -} - -type RoundNumberView struct{} - -func (f RoundNumberView) Call() RoundNumberResults { - // TODO DoCall(nil) instead of client.NewResults() - return RoundNumberResults{res: client.NewResults()} -} - -type RoundNumberResults struct { - res client.Results -} - -func (r RoundNumberResults) RoundNumber() int64 { - return r.res.GetInt64(ResRoundNumber) -} - -type RoundStartedAtView struct{} - -func (f RoundStartedAtView) Call() RoundStartedAtResults { - // TODO DoCall(nil) instead of client.NewResults() - return RoundStartedAtResults{res: client.NewResults()} -} - -type RoundStartedAtResults struct { - res client.Results -} - -func (r RoundStartedAtResults) RoundStartedAt() int32 { - return r.res.GetInt32(ResRoundStartedAt) -} - -type RoundStatusView struct{} - -func (f RoundStatusView) Call() RoundStatusResults { - // TODO DoCall(nil) instead of client.NewResults() - return RoundStatusResults{res: client.NewResults()} -} - -type RoundStatusResults struct { - res client.Results -} - -func (r RoundStatusResults) RoundStatus() int16 { - return r.res.GetInt16(ResRoundStatus) -} - -type FairRouletteService struct { - client.Service -} - -func NewFairRouletteService(cl client.ServiceClient, chainID string) *FairRouletteService { - s := &FairRouletteService{} - s.Service.Init(cl, chainID, "df79d138", EventHandlers) - return s -} - -func (s *FairRouletteService) ForcePayout() ForcePayoutFunc { - return ForcePayoutFunc{} -} - -func (s *FairRouletteService) ForceReset() ForceResetFunc { - return ForceResetFunc{} -} - -func (s *FairRouletteService) PayWinners() PayWinnersFunc { - return PayWinnersFunc{} -} - -func (s *FairRouletteService) PlaceBet() PlaceBetFunc { - return PlaceBetFunc{} -} - -func (s *FairRouletteService) PlayPeriod() PlayPeriodFunc { - return PlayPeriodFunc{} -} - -func (s *FairRouletteService) LastWinningNumber() LastWinningNumberView { - return LastWinningNumberView{} -} - -func (s *FairRouletteService) RoundNumber() RoundNumberView { - return RoundNumberView{} -} - -func (s *FairRouletteService) RoundStartedAt() RoundStartedAtView { - return RoundStartedAtView{} -} - -func (s *FairRouletteService) RoundStatus() RoundStatusView { - return RoundStatusView{} -} diff --git a/contracts/wasm/fairroulette/client/ts/events.ts b/contracts/wasm/fairroulette/client/ts/events.ts deleted file mode 100644 index 4d6bc1f216..0000000000 --- a/contracts/wasm/fairroulette/client/ts/events.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -import * as client from "wasmlib/client" -import * as app from "./fairroulette" - -export const eventHandlers: client.EventHandlers = { - "fairroulette.bet": onFairRouletteBetThunk, - "fairroulette.payout": onFairRoulettePayoutThunk, - "fairroulette.round": onFairRouletteRoundThunk, - "fairroulette.start": onFairRouletteStartThunk, - "fairroulette.stop": onFairRouletteStopThunk, - "fairroulette.winner": onFairRouletteWinnerThunk, -}; - -export class EventBet extends client.Event { - public address: client.Address; - public amount: client.Int64; - public number: client.Int64; -} - -function onFairRouletteBetThunk(message: string[]) { - let e = new EventBet(message); - e.address = e.nextAddress(); - e.amount = e.nextInt64(); - e.number = e.nextInt64(); - app.onFairRouletteBet(e); -} - -export class EventPayout extends client.Event { - public address: client.Address; - public amount: client.Int64; -} - -function onFairRoulettePayoutThunk(message: string[]) { - let e = new EventPayout(message); - e.address = e.nextAddress(); - e.amount = e.nextInt64(); - app.onFairRoulettePayout(e); -} - -export class EventRound extends client.Event { - public number: client.Int64; -} - -function onFairRouletteRoundThunk(message: string[]) { - let e = new EventRound(message); - e.number = e.nextInt64(); - app.onFairRouletteRound(e); -} - -export class EventStart extends client.Event { -} - -function onFairRouletteStartThunk(message: string[]) { - let e = new EventStart(message); - app.onFairRouletteStart(e); -} - -export class EventStop extends client.Event { -} - -function onFairRouletteStopThunk(message: string[]) { - let e = new EventStop(message); - app.onFairRouletteStop(e); -} - -export class EventWinner extends client.Event { - public number: client.Int64; -} - -function onFairRouletteWinnerThunk(message: string[]) { - let e = new EventWinner(message); - e.number = e.nextInt64(); - app.onFairRouletteWinner(e); -} diff --git a/contracts/wasm/fairroulette/client/ts/fairroulette.ts b/contracts/wasm/fairroulette/client/ts/fairroulette.ts deleted file mode 100644 index 45895bb311..0000000000 --- a/contracts/wasm/fairroulette/client/ts/fairroulette.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import * as events from "./events" -import * as service from "./service" - -const client = new BasicClient(config); -const fairRouletteService = new service.FairRouletteService(client, config.ChainId); - -export function onFairRouletteBet(event: events.EventBet): void { -} - -export function onFairRoulettePayout(event: events.EventPayout): void { -} - -export function onFairRouletteRound(event: events.EventRound): void { -} - -export function onFairRouletteStart(event: events.EventStart): void { -} - -export function onFairRouletteStop(event: events.EventStop): void { -} - -export function onFairRouletteWinner(event: events.EventWinner): void { -} diff --git a/contracts/wasm/fairroulette/client/ts/service.ts b/contracts/wasm/fairroulette/client/ts/service.ts deleted file mode 100644 index fa9d42792f..0000000000 --- a/contracts/wasm/fairroulette/client/ts/service.ts +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -import * as client from "wasmlib/client" -import * as events from "./events" - -const ArgNumber = "number"; -const ArgPlayPeriod = "playPeriod"; - -const ResLastWinningNumber = "lastWinningNumber"; -const ResRoundNumber = "roundNumber"; -const ResRoundStartedAt = "roundStartedAt"; -const ResRoundStatus = "roundStatus"; - -export class ForcePayoutFunc { - - post(): void { - //TODO DoPost(null) - } -} - -export class ForceResetFunc { - - post(): void { - //TODO DoPost(null) - } -} - -export class PayWinnersFunc { - - post(): void { - //TODO DoPost(null) - } -} - -export class PlaceBetFunc { - args: client.Arguments = new client.Arguments(); - - number(v: client.Int64): void { - this.args.setInt64(ArgNumber, v); - } - - post(): void { - this.args.mandatory(ArgNumber); - //TODO DoPost(this.args) - } -} - -export class PlayPeriodFunc { - args: client.Arguments = new client.Arguments(); - - playPeriod(v: client.Int32): void { - this.args.setInt32(ArgPlayPeriod, v); - } - - post(): void { - this.args.mandatory(ArgPlayPeriod); - //TODO DoPost(this.args) - } -} - -export class LastWinningNumberView { - - call(): LastWinningNumberResults { - //TODO DoCall(null) instead of new client.Results() - return new LastWinningNumberResults(new client.Results()); - } -} - -export class LastWinningNumberResults { - res: client.Results; - - constructor(res: client.Results) { this.res = res; } - - lastWinningNumber(): client.Int64 { - return this.res.getInt64(ResLastWinningNumber); - } -} - -export class RoundNumberView { - - call(): RoundNumberResults { - //TODO DoCall(null) instead of new client.Results() - return new RoundNumberResults(new client.Results()); - } -} - -export class RoundNumberResults { - res: client.Results; - - constructor(res: client.Results) { this.res = res; } - - roundNumber(): client.Int64 { - return this.res.getInt64(ResRoundNumber); - } -} - -export class RoundStartedAtView { - - call(): RoundStartedAtResults { - //TODO DoCall(null) instead of new client.Results() - return new RoundStartedAtResults(new client.Results()); - } -} - -export class RoundStartedAtResults { - res: client.Results; - - constructor(res: client.Results) { this.res = res; } - - roundStartedAt(): client.Int32 { - return this.res.getInt32(ResRoundStartedAt); - } -} - -export class RoundStatusView { - - call(): RoundStatusResults { - //TODO DoCall(null) instead of new client.Results() - return new RoundStatusResults(new client.Results()); - } -} - -export class RoundStatusResults { - res: client.Results; - - constructor(res: client.Results) { this.res = res; } - - roundStatus(): client.Int16 { - return this.res.getInt16(ResRoundStatus); - } -} - -export class FairRouletteService extends client.Service { - - constructor(cl: client.ServiceClient, chainID: string) { - super(cl, chainID, "df79d138", events.eventHandlers); - } - - public forcePayout(): ForcePayoutFunc { - return new ForcePayoutFunc(); - } - - public forceReset(): ForceResetFunc { - return new ForceResetFunc(); - } - - public payWinners(): PayWinnersFunc { - return new PayWinnersFunc(); - } - - public placeBet(): PlaceBetFunc { - return new PlaceBetFunc(); - } - - public playPeriod(): PlayPeriodFunc { - return new PlayPeriodFunc(); - } - - public lastWinningNumber(): LastWinningNumberView { - return new LastWinningNumberView(); - } - - public roundNumber(): RoundNumberView { - return new RoundNumberView(); - } - - public roundStartedAt(): RoundStartedAtView { - return new RoundStartedAtView(); - } - - public roundStatus(): RoundStatusView { - return new RoundStatusView(); - } -} diff --git a/contracts/wasm/fairroulette/client/ts/tsconfig.json b/contracts/wasm/fairroulette/client/ts/tsconfig.json deleted file mode 100644 index b5353d085d..0000000000 --- a/contracts/wasm/fairroulette/client/ts/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "lib": ["es2020"], - "target": "es2020", - "sourceMap": true - }, - "exclude": [ - "node_modules" - ], -} \ No newline at end of file diff --git a/tools/schema/generator/emitter.go b/tools/schema/generator/emitter.go index efca197f31..e53e8f3747 100644 --- a/tools/schema/generator/emitter.go +++ b/tools/schema/generator/emitter.go @@ -208,7 +208,8 @@ func (g *GenBase) emitEachFunc(funcs []*model.Func, template string) { func (g *GenBase) emitEachMandatoryField(template string) { mandatoryFields := make([]*model.Field, 0) for _, g.currentField = range g.currentFunc.Params { - if !g.currentField.Optional { + fld := g.currentField + if !fld.Optional && fld.TypeID != 0 && !fld.Array && fld.MapKey == "" { mandatoryFields = append(mandatoryFields, g.currentField) } } diff --git a/tools/schema/generator/generator_tsclient.go b/tools/schema/generator/generator_tsclient.go index f4b475deab..e7ec9b26c8 100644 --- a/tools/schema/generator/generator_tsclient.go +++ b/tools/schema/generator/generator_tsclient.go @@ -17,3 +17,22 @@ func NewTsClientGenerator(s *model.Schema) *TsClientGenerator { g.init(s, tsclienttemplates.TypeDependent, tsclienttemplates.Templates) return g } + +func (g *TsClientGenerator) Generate() error { + err := g.ClientBase.Generate() + if err != nil { + return err + } + + // now generate language-specific files + + err = g.createSourceFile("index", true) + if err != nil { + return err + } + + tsconfig := "tsconfig.json" + return g.createFile(g.folder+tsconfig, false, func() { + g.emit(tsconfig) + }) +} diff --git a/tools/schema/generator/goclienttemplates/alltemplates.go b/tools/schema/generator/goclienttemplates/alltemplates.go index 6861075815..47e6be9a2c 100644 --- a/tools/schema/generator/goclienttemplates/alltemplates.go +++ b/tools/schema/generator/goclienttemplates/alltemplates.go @@ -10,10 +10,9 @@ var config = map[string]string{ } var Templates = []map[string]string{ - config, - common, - funcsGo, + config, // always first one eventsGo, + funcsGo, serviceGo, } @@ -157,9 +156,3 @@ var TypeDependent = model.StringMapMap{ "Uint64": "readBigUInt64LE(0)", }, } - -var common = map[string]string{ - // ******************************* - "tmp": ` -tmp`, -} diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index 1b6bcd7a1a..c2fe967dcd 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -64,7 +64,7 @@ func (f $FuncName$Kind) $FldName(v $fldLangType) { "funcPost": ` func (f $FuncName$Kind) Post() { -$#each param mandatoryCheck +$#each mandatory mandatoryCheck $#set exec f.svc.PostRequest $#if param execWithArgs execNoArgs $exec @@ -74,7 +74,7 @@ $#if param execWithArgs execNoArgs "viewCall": ` func (f $FuncName$Kind) Call() $FuncName$+Results { -$#each param mandatoryCheck +$#each mandatory mandatoryCheck $#set exec f.svc.CallView $#if param execWithArgs execNoArgs return $FuncName$+Results { res: $exec } diff --git a/tools/schema/generator/gotemplates/alltemplates.go b/tools/schema/generator/gotemplates/alltemplates.go index f4d0fd736b..3fe42a9552 100644 --- a/tools/schema/generator/gotemplates/alltemplates.go +++ b/tools/schema/generator/gotemplates/alltemplates.go @@ -10,7 +10,7 @@ var config = map[string]string{ } var Templates = []map[string]string{ - config, + config, // always first one common, constsGo, contractGo, diff --git a/tools/schema/generator/gotemplates/proxy.go b/tools/schema/generator/gotemplates/proxy.go index 4ea6a2f3f0..7d91347225 100644 --- a/tools/schema/generator/gotemplates/proxy.go +++ b/tools/schema/generator/gotemplates/proxy.go @@ -48,7 +48,7 @@ func (s $TypeName) $FldName() Map$fldMapKey$+To$mut$FldType { } `, // ******************************* - "proxyMapOther": `55544444.0 + "proxyMapOther": ` func (s $TypeName) $FldName() Map$fldMapKey$+To$mut$FldType { mapID := wasmlib.GetObjectID(s.id, $varID, wasmlib.TYPE_MAP) diff --git a/tools/schema/generator/rstemplates/alltemplates.go b/tools/schema/generator/rstemplates/alltemplates.go index 394bf05480..0764cd0b16 100644 --- a/tools/schema/generator/rstemplates/alltemplates.go +++ b/tools/schema/generator/rstemplates/alltemplates.go @@ -10,7 +10,7 @@ var config = map[string]string{ } var Templates = []map[string]string{ - config, + config, // always first one common, cargoToml, constsRs, diff --git a/tools/schema/generator/rstemplates/proxy.go b/tools/schema/generator/rstemplates/proxy.go index 9070f680a3..d18fb51f0a 100644 --- a/tools/schema/generator/rstemplates/proxy.go +++ b/tools/schema/generator/rstemplates/proxy.go @@ -48,7 +48,7 @@ $#if this proxyMapThis proxyMapOther } `, // ******************************* - "proxyMapOther": `55544444.0 + "proxyMapOther": ` pub fn $fld_name(&self) -> Map$fldMapKey$+To$mut$FldType { let map_id = get_object_id(self.id, $varID, TYPE_MAP); Map$fldMapKey$+To$mut$FldType { obj_id: map_id } diff --git a/tools/schema/generator/tsclienttemplates/alltemplates.go b/tools/schema/generator/tsclienttemplates/alltemplates.go index 821fe36d95..7f5470ea5e 100644 --- a/tools/schema/generator/tsclienttemplates/alltemplates.go +++ b/tools/schema/generator/tsclienttemplates/alltemplates.go @@ -10,10 +10,11 @@ var config = map[string]string{ } var Templates = []map[string]string{ - config, + config, // always first one common, - funcsTs, eventsTs, + funcsTs, + indexTs, serviceTs, } @@ -80,6 +81,17 @@ var TypeDependent = model.StringMapMap{ var common = map[string]string{ // ******************************* - "tmp": ` -tmp`, + "tsconfig.json": ` +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es2020"], + "target": "es2020", + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], +} +`, } diff --git a/tools/schema/generator/tsclienttemplates/index.go b/tools/schema/generator/tsclienttemplates/index.go new file mode 100644 index 0000000000..5c075efe59 --- /dev/null +++ b/tools/schema/generator/tsclienttemplates/index.go @@ -0,0 +1,10 @@ +package tsclienttemplates + +var indexTs = map[string]string{ + // ******************************* + "index.ts": ` +export * from "./$package"; +export * from "./events"; +export * from "./service"; +`, +} diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index f88ef71d65..cceb40dbb3 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -56,7 +56,7 @@ $#if result resultStruct "funcPost": ` public async post(): Promise { -$#each param mandatoryCheck +$#each mandatory mandatoryCheck $#set exec this.svc.postRequest $#if param execWithArgs execNoArgs $exec; @@ -66,7 +66,7 @@ $#if param execWithArgs execNoArgs "viewCall": ` public async call(): Promise<$FuncName$+Results> { -$#each param mandatoryCheck +$#each mandatory mandatoryCheck $#set exec this.svc.callView $#if param execWithArgs execNoArgs return new $FuncName$+Results($exec); diff --git a/tools/schema/generator/tstemplates/alltemplates.go b/tools/schema/generator/tstemplates/alltemplates.go index 8fc4865ace..9fa0a18071 100644 --- a/tools/schema/generator/tstemplates/alltemplates.go +++ b/tools/schema/generator/tstemplates/alltemplates.go @@ -10,7 +10,7 @@ var config = map[string]string{ } var Templates = []map[string]string{ - config, + config, // always first one common, constsTs, contractTs, From afe8aa1c5695358989632a3f119f6a0fffc2e097 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 22 Dec 2021 20:58:08 -0800 Subject: [PATCH 016/120] Implement client params arrays --- packages/vm/wasmlib/go/wasmlib/client/arguments.go | 5 +++++ packages/vm/wasmlib/ts/wasmlib/client/arguments.ts | 6 +++++- .../schema/generator/goclienttemplates/service.go | 14 ++++++++++++++ .../schema/generator/tsclienttemplates/service.go | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/vm/wasmlib/go/wasmlib/client/arguments.go b/packages/vm/wasmlib/go/wasmlib/client/arguments.go index a95e5703f2..1d3540c7f7 100644 --- a/packages/vm/wasmlib/go/wasmlib/client/arguments.go +++ b/packages/vm/wasmlib/go/wasmlib/client/arguments.go @@ -2,6 +2,7 @@ package client import ( "sort" + "strconv" "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" @@ -28,6 +29,10 @@ func (a Arguments) setBase58(key, val string, typeID int32) { a.set(key, bytes) } +func (a Arguments) IndexedKey(key string, index int) string { + return key + "." + strconv.Itoa(index) +} + func (a Arguments) Mandatory(key string) { if a.args != nil { if _, ok := a.args[key]; ok { diff --git a/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts b/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts index 6ebff11354..dbbc38b9d7 100644 --- a/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts @@ -24,7 +24,11 @@ export class Arguments { this.set(key, bytes); } - mandatory(key:string): void { + indexedKey(key: string, index: client.Int32): string { + return key + "." + index.toString(); + } + + mandatory(key: string): void { if (!this.args.has(key)) { client.panic("missing mandatory " + key) } diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index c2fe967dcd..973eaf7fb1 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -55,10 +55,24 @@ $#if func funcPost viewCall `, // ******************************* "funcArgSetter": ` +$#if array funcArgSetterArray funcArgSetterBasic +`, + // ******************************* + "funcArgSetterBasic": ` func (f $FuncName$Kind) $FldName(v $fldLangType) { f.args.Set$FldType(Arg$FldName, v) } +`, + // ******************************* + "funcArgSetterArray": ` + +func (f $FuncName$Kind) $FldName(a []$fldLangType) { + for i, v := range a { + f.args.Set$FldType(f.args.IndexedKey(Arg$FldName, i), v) + } + f.args.SetInt32(Arg$FldName, int32(len(a))) +} `, // ******************************* "funcPost": ` diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index cceb40dbb3..ff7986536c 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -47,10 +47,24 @@ $#if result resultStruct `, // ******************************* "funcArgSetter": ` +$#if array funcArgSetterArray funcArgSetterBasic +`, + // ******************************* + "funcArgSetterBasic": ` $fldName(v: $fldLangType): void { this.args.set$FldType(Arg$FldName, v); } +`, + // ******************************* + "funcArgSetterArray": ` + + $fldName(a: $fldLangType[]): void { + for (let i = 0; i < a.length; i++) { + this.args.set$FldType(this.args.indexedKey(Arg$FldName, i), a[i]); + } + this.args.setInt32(Arg$FldName, a.length); + } `, // ******************************* "funcPost": ` From 706e2bf7a3b250bf661aa665a5baf6f691eac9be Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Thu, 23 Dec 2021 14:10:02 +0200 Subject: [PATCH 017/120] Make nodeconn metrics more beautiful --- packages/dashboard/base.go | 1 + packages/dashboard/templates/base.tmpl | 5 +++++ .../templates/metrics_nodeconn_messages.tmpl | 18 +++++++++--------- packages/dashboard/util.go | 7 +++++++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/dashboard/base.go b/packages/dashboard/base.go index 7e2b41737d..d6b3f477e3 100644 --- a/packages/dashboard/base.go +++ b/packages/dashboard/base.go @@ -108,6 +108,7 @@ func (d *Dashboard) makeTemplate(e *echo.Echo, parts ...string) *template.Templa "decUint32": decUint32, "bytesToString": bytesToString, "keyToString": keyToString, + "anythingToString": anythingToString, "base58": base58.Encode, "replace": strings.Replace, "uri": func(s string, p ...interface{}) string { return e.Reverse(s, p...) }, diff --git a/packages/dashboard/templates/base.tmpl b/packages/dashboard/templates/base.tmpl index a8b2cb3297..449f2fe346 100644 --- a/packages/dashboard/templates/base.tmpl +++ b/packages/dashboard/templates/base.tmpl @@ -39,6 +39,11 @@ {{end}} +{{define "longStringCell"}} + {{ $longString := anythingToString . }} + {{ trim 100 (replace $longString "\n" "" -1) }} +{{end}} + {{define "base"}} diff --git a/packages/dashboard/templates/metrics_nodeconn_messages.tmpl b/packages/dashboard/templates/metrics_nodeconn_messages.tmpl index b3115cdae5..93b32e73e5 100644 --- a/packages/dashboard/templates/metrics_nodeconn_messages.tmpl +++ b/packages/dashboard/templates/metrics_nodeconn_messages.tmpl @@ -16,62 +16,62 @@ OUT {{ (($metrics.GetOutPullState).GetMessageTotal) }} {{ (formatTimestampOrNever (($metrics.GetOutPullState).GetLastEvent)) }} - {{ (($metrics.GetOutPullState).GetLastMessage) }} + {{ template "longStringCell" (($metrics.GetOutPullState).GetLastMessage)}} Pull tx inclusion state OUT {{ (($metrics.GetOutPullTransactionInclusionState).GetMessageTotal) }} {{ (formatTimestampOrNever (($metrics.GetOutPullTransactionInclusionState).GetLastEvent)) }} - {{ (($metrics.GetOutPullTransactionInclusionState).GetLastMessage) }} + {{ template "longStringCell" (($metrics.GetOutPullTransactionInclusionState).GetLastMessage)}} Pull confirmed output OUT {{ (($metrics.GetOutPullConfirmedOutput).GetMessageTotal) }} {{ (formatTimestampOrNever (($metrics.GetOutPullConfirmedOutput).GetLastEvent)) }} - {{ (($metrics.GetOutPullConfirmedOutput).GetLastMessage) }} + {{ template "longStringCell" (($metrics.GetOutPullConfirmedOutput).GetLastMessage)}} Post transaction OUT {{ (($metrics.GetOutPostTransaction).GetMessageTotal) }} {{ (formatTimestampOrNever (($metrics.GetOutPostTransaction).GetLastEvent)) }} - {{ (($metrics.GetOutPostTransaction).GetLastMessage) }} + {{ template "longStringCell" (($metrics.GetOutPostTransaction).GetLastMessage)}} Transaction IN {{ (($metrics.GetInTransaction).GetMessageTotal) }} {{ (formatTimestampOrNever (($metrics.GetInTransaction).GetLastEvent)) }} - {{ (($metrics.GetInTransaction).GetLastMessage) }} + {{ template "longStringCell" (($metrics.GetInTransaction).GetLastMessage)}} Inclusion state IN {{ (($metrics.GetInInclusionState).GetMessageTotal) }} {{ (formatTimestampOrNever (($metrics.GetInInclusionState).GetLastEvent)) }} - {{ (($metrics.GetInInclusionState).GetLastMessage) }} + {{ template "longStringCell" (($metrics.GetInInclusionState).GetLastMessage)}} Output IN {{ (($metrics.GetInOutput).GetMessageTotal) }} {{ (formatTimestampOrNever (($metrics.GetInOutput).GetLastEvent)) }} - {{ (($metrics.GetInOutput).GetLastMessage) }} + {{ template "longStringCell" (($metrics.GetInOutput).GetLastMessage)}} Unspent alias output IN {{ (($metrics.GetInUnspentAliasOutput).GetMessageTotal) }} {{ (formatTimestampOrNever (($metrics.GetInUnspentAliasOutput).GetLastEvent)) }} - {{ (($metrics.GetInUnspentAliasOutput).GetLastMessage) }} + {{ template "longStringCell" (($metrics.GetInUnspentAliasOutput).GetLastMessage)}} {{end}} -{{define "title"}}Kažkas{{end}} +{{define "title"}}Connection to L1 metrics{{end}} {{define "body"}}
diff --git a/packages/dashboard/util.go b/packages/dashboard/util.go index a646869c80..35267ee95d 100644 --- a/packages/dashboard/util.go +++ b/packages/dashboard/util.go @@ -48,6 +48,13 @@ func bytesToString(b []byte) string { return string(b) } +func anythingToString(i interface{}) string { + if i == nil { + return "" + } + return fmt.Sprintf("%v", i) +} + func formatTimestamp(ts interface{}) string { t, ok := ts.(time.Time) if !ok { From 2921b99899173627cb885adb60bdcda560c011bb Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Fri, 24 Dec 2021 22:05:33 -0800 Subject: [PATCH 018/120] Reshuffle and rename --- contracts/wasm/core_build.cmd | 2 + contracts/wasm/dividend/go/main.go | 4 +- contracts/wasm/donatewithfeedback/go/main.go | 4 +- contracts/wasm/erc20/go/main.go | 4 +- contracts/wasm/erc721/go/main.go | 4 +- contracts/wasm/fairauction/go/main.go | 4 +- contracts/wasm/fairroulette/go/main.go | 4 +- contracts/wasm/helloworld/go/main.go | 4 +- contracts/wasm/inccounter/go/main.go | 4 +- contracts/wasm/testcore/go/main.go | 4 +- contracts/wasm/testwasmlib/go/main.go | 4 +- .../wasm/testwasmlib/go/testwasmlib/consts.go | 2 + .../testwasmlib/go/testwasmlib/contract.go | 11 + .../wasm/testwasmlib/go/testwasmlib/events.go | 19 + .../wasm/testwasmlib/go/testwasmlib/lib.go | 30 +- .../wasm/testwasmlib/go/testwasmlib/params.go | 24 + .../testwasmlib/go/testwasmlib/testwasmlib.go | 4 + .../go/testwasmlibclient/events.go | 28 ++ .../go/testwasmlibclient/service.go | 422 ++++++++++++++++++ .../go/testwasmlibclient/testwasmlib.go | 7 + contracts/wasm/testwasmlib/schema.yaml | 8 + contracts/wasm/testwasmlib/src/consts.rs | 2 + contracts/wasm/testwasmlib/src/contract.rs | 14 + contracts/wasm/testwasmlib/src/events.rs | 23 + contracts/wasm/testwasmlib/src/lib.rs | 36 ++ contracts/wasm/testwasmlib/src/params.rs | 30 ++ contracts/wasm/testwasmlib/src/testwasmlib.rs | 4 + .../wasm/testwasmlib/test/testwasmlib_bg.wasm | Bin 41341 -> 43545 bytes .../wasm/testwasmlib/test/testwasmlib_test.go | 5 + .../wasm/testwasmlib/ts/testwasmlib/consts.ts | 2 + .../testwasmlib/ts/testwasmlib/contract.ts | 22 + .../wasm/testwasmlib/ts/testwasmlib/events.ts | 18 + .../wasm/testwasmlib/ts/testwasmlib/index.ts | 1 + .../wasm/testwasmlib/ts/testwasmlib/lib.ts | 12 + .../wasm/testwasmlib/ts/testwasmlib/params.ts | 20 + .../testwasmlib/ts/testwasmlib/testwasmlib.ts | 4 + .../ts/testwasmlibclient/events.ts | 25 ++ .../testwasmlib/ts/testwasmlibclient/index.ts | 10 + .../ts/testwasmlibclient/service.ts | 398 +++++++++++++++++ .../ts/testwasmlibclient/testwasmlib.ts | 11 + .../ts/testwasmlibclient/tsconfig.json | 11 + contracts/wasm/timestamp/go/main.go | 4 +- contracts/wasm/tokenregistry/go/main.go | 4 +- .../client => wasmclient}/arguments.go | 48 +- .../{wasmlib/client => wasmclient}/event.go | 2 +- packages/vm/wasmlib/go/wasmclient/request.go | 14 + .../{wasmlib/client => wasmclient}/results.go | 15 +- packages/vm/wasmlib/go/wasmclient/service.go | 96 ++++ .../{wasmlib/client => wasmclient}/types.go | 2 +- .../vm/wasmlib/go/wasmlib/client/service.go | 35 -- .../client => wasmclient}/arguments.ts | 4 +- .../client => wasmclient}/buffer/index.d.ts | 0 .../client => wasmclient}/buffer/index.js | 0 .../client => wasmclient}/buffer/readme.md | 0 .../client => wasmclient}/config.dev.js | 0 .../client => wasmclient}/crypto/base58.ts | 0 .../client => wasmclient}/crypto/ed25519.ts | 0 .../client => wasmclient}/crypto/hname.ts | 0 .../client => wasmclient}/crypto/index.ts | 0 .../client => wasmclient}/crypto/seed.ts | 0 .../{wasmlib/client => wasmclient}/event.ts | 0 .../{wasmlib/client => wasmclient}/index.ts | 0 .../{wasmlib/client => wasmclient}/results.ts | 2 +- .../{wasmlib/client => wasmclient}/service.ts | 4 +- .../client => wasmclient}/tsconfig.json | 0 .../{wasmlib/client => wasmclient}/types.ts | 0 .../vm/{wasmclient => wasmvmhost}/host.go | 2 +- tools/schema/generator/clientbase.go | 6 +- .../goclienttemplates/alltemplates.go | 54 ++- .../generator/goclienttemplates/events.go | 8 +- .../generator/goclienttemplates/funcs.go | 6 +- .../generator/goclienttemplates/service.go | 30 +- tools/schema/generator/gotemplates/main.go | 4 +- .../tsclienttemplates/alltemplates.go | 34 +- .../generator/tsclienttemplates/events.go | 8 +- .../generator/tsclienttemplates/service.go | 14 +- 76 files changed, 1447 insertions(+), 194 deletions(-) create mode 100644 contracts/wasm/testwasmlib/go/testwasmlib/events.go create mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/events.go create mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/service.go create mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go create mode 100644 contracts/wasm/testwasmlib/src/events.rs create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlib/events.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json rename packages/vm/wasmlib/go/{wasmlib/client => wasmclient}/arguments.go (67%) rename packages/vm/wasmlib/go/{wasmlib/client => wasmclient}/event.go (98%) create mode 100644 packages/vm/wasmlib/go/wasmclient/request.go rename packages/vm/wasmlib/go/{wasmlib/client => wasmclient}/results.go (91%) create mode 100644 packages/vm/wasmlib/go/wasmclient/service.go rename packages/vm/wasmlib/go/{wasmlib/client => wasmclient}/types.go (87%) delete mode 100644 packages/vm/wasmlib/go/wasmlib/client/service.go rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/arguments.ts (96%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/buffer/index.d.ts (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/buffer/index.js (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/buffer/readme.md (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/config.dev.js (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/crypto/base58.ts (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/crypto/ed25519.ts (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/crypto/hname.ts (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/crypto/index.ts (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/crypto/seed.ts (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/event.ts (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/index.ts (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/results.ts (98%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/service.ts (93%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/tsconfig.json (100%) rename packages/vm/wasmlib/ts/{wasmlib/client => wasmclient}/types.ts (100%) rename packages/vm/{wasmclient => wasmvmhost}/host.go (99%) diff --git a/contracts/wasm/core_build.cmd b/contracts/wasm/core_build.cmd index 79d5f2e936..6db737fb30 100644 --- a/contracts/wasm/core_build.cmd +++ b/contracts/wasm/core_build.cmd @@ -2,5 +2,7 @@ cd ..\..\packages\vm\wasmlib schema -core -go -rust -ts -force del /s /q d:\work\node_modules\wasmlib\*.* >nul: +del /s /q d:\work\node_modules\wasmclient\*.* >nul: +xcopy /s /q d:\Work\go\github.com\iotaledger\wasp\packages\vm\wasmlib\ts\wasmclient d:\work\node_modules\wasmclient xcopy /s /q d:\Work\go\github.com\iotaledger\wasp\packages\vm\wasmlib\ts\wasmlib d:\work\node_modules\wasmlib cd ..\..\..\contracts\wasm diff --git a/contracts/wasm/dividend/go/main.go b/contracts/wasm/dividend/go/main.go index 13ca6f38f9..2b86f655a4 100644 --- a/contracts/wasm/dividend/go/main.go +++ b/contracts/wasm/dividend/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/dividend/go/dividend" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() dividend.OnLoad() } diff --git a/contracts/wasm/donatewithfeedback/go/main.go b/contracts/wasm/donatewithfeedback/go/main.go index a2260be5b6..a263164081 100644 --- a/contracts/wasm/donatewithfeedback/go/main.go +++ b/contracts/wasm/donatewithfeedback/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/donatewithfeedback/go/donatewithfeedback" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() donatewithfeedback.OnLoad() } diff --git a/contracts/wasm/erc20/go/main.go b/contracts/wasm/erc20/go/main.go index 876c78787d..ebb45b8642 100644 --- a/contracts/wasm/erc20/go/main.go +++ b/contracts/wasm/erc20/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/erc20/go/erc20" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() erc20.OnLoad() } diff --git a/contracts/wasm/erc721/go/main.go b/contracts/wasm/erc721/go/main.go index b863c75bb9..f06a3059cc 100644 --- a/contracts/wasm/erc721/go/main.go +++ b/contracts/wasm/erc721/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/erc721/go/erc721" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() erc721.OnLoad() } diff --git a/contracts/wasm/fairauction/go/main.go b/contracts/wasm/fairauction/go/main.go index be0f4b3c1b..031df458fb 100644 --- a/contracts/wasm/fairauction/go/main.go +++ b/contracts/wasm/fairauction/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/fairauction/go/fairauction" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() fairauction.OnLoad() } diff --git a/contracts/wasm/fairroulette/go/main.go b/contracts/wasm/fairroulette/go/main.go index 17d9e08530..c4f5e312dc 100644 --- a/contracts/wasm/fairroulette/go/main.go +++ b/contracts/wasm/fairroulette/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/fairroulette/go/fairroulette" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() fairroulette.OnLoad() } diff --git a/contracts/wasm/helloworld/go/main.go b/contracts/wasm/helloworld/go/main.go index e91b27bf55..856ecebec4 100644 --- a/contracts/wasm/helloworld/go/main.go +++ b/contracts/wasm/helloworld/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/helloworld/go/helloworld" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() helloworld.OnLoad() } diff --git a/contracts/wasm/inccounter/go/main.go b/contracts/wasm/inccounter/go/main.go index 7a5a3f98bd..47c9e2a29f 100644 --- a/contracts/wasm/inccounter/go/main.go +++ b/contracts/wasm/inccounter/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/inccounter/go/inccounter" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() inccounter.OnLoad() } diff --git a/contracts/wasm/testcore/go/main.go b/contracts/wasm/testcore/go/main.go index 44c2dc0972..9d0d44a9eb 100644 --- a/contracts/wasm/testcore/go/main.go +++ b/contracts/wasm/testcore/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/testcore/go/testcore" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() testcore.OnLoad() } diff --git a/contracts/wasm/testwasmlib/go/main.go b/contracts/wasm/testwasmlib/go/main.go index c213bd8591..5138f55c33 100644 --- a/contracts/wasm/testwasmlib/go/main.go +++ b/contracts/wasm/testwasmlib/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlib" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() testwasmlib.OnLoad() } diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/consts.go b/contracts/wasm/testwasmlib/go/testwasmlib/consts.go index b28384f1f9..bfafdc9086 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/consts.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/consts.go @@ -62,6 +62,7 @@ const ( FuncArraySet = "arraySet" FuncParamTypes = "paramTypes" FuncRandom = "random" + FuncTriggerEvent = "triggerEvent" ViewArrayLength = "arrayLength" ViewArrayValue = "arrayValue" ViewBlockRecord = "blockRecord" @@ -76,6 +77,7 @@ const ( HFuncArraySet = wasmlib.ScHname(0x2c4150b3) HFuncParamTypes = wasmlib.ScHname(0x6921c4cd) HFuncRandom = wasmlib.ScHname(0xe86c97ca) + HFuncTriggerEvent = wasmlib.ScHname(0xd5438ac6) HViewArrayLength = wasmlib.ScHname(0x3a831021) HViewArrayValue = wasmlib.ScHname(0x662dbd81) HViewBlockRecord = wasmlib.ScHname(0xad13b2f8) diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/contract.go b/contracts/wasm/testwasmlib/go/testwasmlib/contract.go index 816dc14035..7d1d33c83f 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/contract.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/contract.go @@ -33,6 +33,11 @@ type RandomCall struct { Func *wasmlib.ScFunc } +type TriggerEventCall struct { + Func *wasmlib.ScFunc + Params MutableTriggerEventParams +} + type ArrayLengthCall struct { Func *wasmlib.ScView Params MutableArrayLengthParams @@ -99,6 +104,12 @@ func (sc Funcs) Random(ctx wasmlib.ScFuncCallContext) *RandomCall { return &RandomCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncRandom)} } +func (sc Funcs) TriggerEvent(ctx wasmlib.ScFuncCallContext) *TriggerEventCall { + f := &TriggerEventCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncTriggerEvent)} + f.Func.SetPtrs(&f.Params.id, nil) + return f +} + func (sc Funcs) ArrayLength(ctx wasmlib.ScViewCallContext) *ArrayLengthCall { f := &ArrayLengthCall{Func: wasmlib.NewScView(ctx, HScName, HViewArrayLength)} f.Func.SetPtrs(&f.Params.id, &f.Results.id) diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/events.go b/contracts/wasm/testwasmlib/go/testwasmlib/events.go new file mode 100644 index 0000000000..eb9574346e --- /dev/null +++ b/contracts/wasm/testwasmlib/go/testwasmlib/events.go @@ -0,0 +1,19 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package testwasmlib + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" + +type TestWasmLibEvents struct{} + +func (e TestWasmLibEvents) Test(address wasmlib.ScAddress, name string) { + wasmlib.NewEventEncoder("testwasmlib.test"). + Address(address). + String(name). + Emit() +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/lib.go b/contracts/wasm/testwasmlib/go/testwasmlib/lib.go index 19543d3317..fc9a32ca7d 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/lib.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/lib.go @@ -16,6 +16,7 @@ func OnLoad() { exports.AddFunc(FuncArraySet, funcArraySetThunk) exports.AddFunc(FuncParamTypes, funcParamTypesThunk) exports.AddFunc(FuncRandom, funcRandomThunk) + exports.AddFunc(FuncTriggerEvent, funcTriggerEventThunk) exports.AddView(ViewArrayLength, viewArrayLengthThunk) exports.AddView(ViewArrayValue, viewArrayValueThunk) exports.AddView(ViewBlockRecord, viewBlockRecordThunk) @@ -29,6 +30,7 @@ func OnLoad() { } type ArrayClearContext struct { + Events TestWasmLibEvents Params ImmutableArrayClearParams State MutableTestWasmLibState } @@ -49,6 +51,7 @@ func funcArrayClearThunk(ctx wasmlib.ScFuncContext) { } type ArrayCreateContext struct { + Events TestWasmLibEvents Params ImmutableArrayCreateParams State MutableTestWasmLibState } @@ -69,6 +72,7 @@ func funcArrayCreateThunk(ctx wasmlib.ScFuncContext) { } type ArraySetContext struct { + Events TestWasmLibEvents Params ImmutableArraySetParams State MutableTestWasmLibState } @@ -91,6 +95,7 @@ func funcArraySetThunk(ctx wasmlib.ScFuncContext) { } type ParamTypesContext struct { + Events TestWasmLibEvents Params ImmutableParamTypesParams State MutableTestWasmLibState } @@ -110,7 +115,8 @@ func funcParamTypesThunk(ctx wasmlib.ScFuncContext) { } type RandomContext struct { - State MutableTestWasmLibState + Events TestWasmLibEvents + State MutableTestWasmLibState } func funcRandomThunk(ctx wasmlib.ScFuncContext) { @@ -124,6 +130,28 @@ func funcRandomThunk(ctx wasmlib.ScFuncContext) { ctx.Log("testwasmlib.funcRandom ok") } +type TriggerEventContext struct { + Events TestWasmLibEvents + Params ImmutableTriggerEventParams + State MutableTestWasmLibState +} + +func funcTriggerEventThunk(ctx wasmlib.ScFuncContext) { + ctx.Log("testwasmlib.funcTriggerEvent") + f := &TriggerEventContext{ + Params: ImmutableTriggerEventParams{ + id: wasmlib.OBJ_ID_PARAMS, + }, + State: MutableTestWasmLibState{ + id: wasmlib.OBJ_ID_STATE, + }, + } + ctx.Require(f.Params.Address().Exists(), "missing mandatory address") + ctx.Require(f.Params.Name().Exists(), "missing mandatory name") + funcTriggerEvent(ctx, f) + ctx.Log("testwasmlib.funcTriggerEvent ok") +} + type ArrayLengthContext struct { Params ImmutableArrayLengthParams Results MutableArrayLengthResults diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/params.go b/contracts/wasm/testwasmlib/go/testwasmlib/params.go index c31fca95d0..cd9cd18ba2 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/params.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/params.go @@ -253,6 +253,30 @@ func (s MutableParamTypesParams) Uint8() wasmlib.ScMutableUint8 { return wasmlib.NewScMutableUint8(s.id, idxMap[IdxParamUint8]) } +type ImmutableTriggerEventParams struct { + id int32 +} + +func (s ImmutableTriggerEventParams) Address() wasmlib.ScImmutableAddress { + return wasmlib.NewScImmutableAddress(s.id, idxMap[IdxParamAddress]) +} + +func (s ImmutableTriggerEventParams) Name() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, idxMap[IdxParamName]) +} + +type MutableTriggerEventParams struct { + id int32 +} + +func (s MutableTriggerEventParams) Address() wasmlib.ScMutableAddress { + return wasmlib.NewScMutableAddress(s.id, idxMap[IdxParamAddress]) +} + +func (s MutableTriggerEventParams) Name() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, idxMap[IdxParamName]) +} + type ImmutableArrayLengthParams struct { id int32 } diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go b/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go index dba9a43d1f..2c8bcd5ac4 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go @@ -133,3 +133,7 @@ func funcRandom(ctx wasmlib.ScFuncContext, f *RandomContext) { func viewGetRandom(ctx wasmlib.ScViewContext, f *GetRandomContext) { f.Results.Random().SetValue(f.State.Random().Value()) } + +func funcTriggerEvent(ctx wasmlib.ScFuncContext, f *TriggerEventContext) { + f.Events.Test(f.Params.Address().Value(), f.Params.Name().Value()) +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go new file mode 100644 index 0000000000..e0c3faf93c --- /dev/null +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go @@ -0,0 +1,28 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package testwasmlibclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +var EventHandlers = map[string]func([]string){ + "testwasmlib.test": onTestWasmLibTestThunk, +} + +type EventTest struct { + wasmclient.Event + Address wasmclient.Address + Name string +} + +func onTestWasmLibTestThunk(message []string) { + e := &EventTest{} + e.Init(message) + e.Address = e.NextAddress() + e.Name = e.NextString() + OnTestWasmLibTest(e) +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go new file mode 100644 index 0000000000..2c82798b87 --- /dev/null +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go @@ -0,0 +1,422 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package testwasmlibclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +const ( + ArgAddress = "address" + ArgAgentID = "agentID" + ArgBlockIndex = "blockIndex" + ArgBool = "bool" + ArgBytes = "bytes" + ArgChainID = "chainID" + ArgColor = "color" + ArgHash = "hash" + ArgHname = "hname" + ArgIndex = "index" + ArgInt16 = "int16" + ArgInt32 = "int32" + ArgInt64 = "int64" + ArgInt8 = "int8" + ArgName = "name" + ArgParam = "this" + ArgRecordIndex = "recordIndex" + ArgRequestID = "requestID" + ArgString = "string" + ArgUint16 = "uint16" + ArgUint32 = "uint32" + ArgUint64 = "uint64" + ArgUint8 = "uint8" + ArgValue = "value" + + ResCount = "count" + ResIotas = "iotas" + ResLength = "length" + ResRandom = "random" + ResRecord = "record" + ResValue = "value" +) + +///////////////////////////// arrayClear ///////////////////////////// + +type ArrayClearFunc struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f ArrayClearFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f ArrayClearFunc) Post(transfer ...map[string]uint64) wasmclient.Request { + f.args.Mandatory(ArgName) + return f.svc.PostRequest(0x88021821, &f.args, transfer...) +} + +///////////////////////////// arrayCreate ///////////////////////////// + +type ArrayCreateFunc struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f ArrayCreateFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f ArrayCreateFunc) Post(transfer ...map[string]uint64) wasmclient.Request { + f.args.Mandatory(ArgName) + return f.svc.PostRequest(0x1ed5b23b, &f.args, transfer...) +} + +///////////////////////////// arraySet ///////////////////////////// + +type ArraySetFunc struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f ArraySetFunc) Index(v int32) { + f.args.SetInt32(ArgIndex, v) +} + +func (f ArraySetFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f ArraySetFunc) Value(v string) { + f.args.SetString(ArgValue, v) +} + +func (f ArraySetFunc) Post(transfer ...map[string]uint64) wasmclient.Request { + f.args.Mandatory(ArgIndex) + f.args.Mandatory(ArgName) + f.args.Mandatory(ArgValue) + return f.svc.PostRequest(0x2c4150b3, &f.args, transfer...) +} + +///////////////////////////// paramTypes ///////////////////////////// + +type ParamTypesFunc struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f ParamTypesFunc) Address(v wasmclient.Address) { + f.args.SetAddress(ArgAddress, v) +} + +func (f ParamTypesFunc) AgentID(v wasmclient.AgentID) { + f.args.SetAgentID(ArgAgentID, v) +} + +func (f ParamTypesFunc) Bool(v bool) { + f.args.SetBool(ArgBool, v) +} + +func (f ParamTypesFunc) Bytes(v []byte) { + f.args.SetBytes(ArgBytes, v) +} + +func (f ParamTypesFunc) ChainID(v wasmclient.ChainID) { + f.args.SetChainID(ArgChainID, v) +} + +func (f ParamTypesFunc) Color(v wasmclient.Color) { + f.args.SetColor(ArgColor, v) +} + +func (f ParamTypesFunc) Hash(v wasmclient.Hash) { + f.args.SetHash(ArgHash, v) +} + +func (f ParamTypesFunc) Hname(v wasmclient.Hname) { + f.args.SetHname(ArgHname, v) +} + +func (f ParamTypesFunc) Int16(v int16) { + f.args.SetInt16(ArgInt16, v) +} + +func (f ParamTypesFunc) Int32(v int32) { + f.args.SetInt32(ArgInt32, v) +} + +func (f ParamTypesFunc) Int64(v int64) { + f.args.SetInt64(ArgInt64, v) +} + +func (f ParamTypesFunc) Int8(v int8) { + f.args.SetInt8(ArgInt8, v) +} + +func (f ParamTypesFunc) Param(v []byte) { + f.args.SetBytes(ArgParam, v) +} + +func (f ParamTypesFunc) RequestID(v wasmclient.RequestID) { + f.args.SetRequestID(ArgRequestID, v) +} + +func (f ParamTypesFunc) String(v string) { + f.args.SetString(ArgString, v) +} + +func (f ParamTypesFunc) Uint16(v uint16) { + f.args.SetUint16(ArgUint16, v) +} + +func (f ParamTypesFunc) Uint32(v uint32) { + f.args.SetUint32(ArgUint32, v) +} + +func (f ParamTypesFunc) Uint64(v uint64) { + f.args.SetUint64(ArgUint64, v) +} + +func (f ParamTypesFunc) Uint8(v uint8) { + f.args.SetUint8(ArgUint8, v) +} + +func (f ParamTypesFunc) Post(transfer ...map[string]uint64) wasmclient.Request { + return f.svc.PostRequest(0x6921c4cd, &f.args, transfer...) +} + +///////////////////////////// random ///////////////////////////// + +type RandomFunc struct { + svc *wasmclient.Service +} + +func (f RandomFunc) Post(transfer ...map[string]uint64) wasmclient.Request { + return f.svc.PostRequest(0xe86c97ca, nil, transfer...) +} + +///////////////////////////// triggerEvent ///////////////////////////// + +type TriggerEventFunc struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f TriggerEventFunc) Address(v wasmclient.Address) { + f.args.SetAddress(ArgAddress, v) +} + +func (f TriggerEventFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f TriggerEventFunc) Post(transfer ...map[string]uint64) wasmclient.Request { + f.args.Mandatory(ArgAddress) + f.args.Mandatory(ArgName) + return f.svc.PostRequest(0xd5438ac6, &f.args, transfer...) +} + +///////////////////////////// arrayLength ///////////////////////////// + +type ArrayLengthView struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f ArrayLengthView) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f ArrayLengthView) Call() ArrayLengthResults { + f.args.Mandatory(ArgName) + return ArrayLengthResults{res: f.svc.CallView("arrayLength", &f.args)} +} + +type ArrayLengthResults struct { + res wasmclient.Results +} + +func (r ArrayLengthResults) Length() int32 { + return r.res.GetInt32(ResLength) +} + +///////////////////////////// arrayValue ///////////////////////////// + +type ArrayValueView struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f ArrayValueView) Index(v int32) { + f.args.SetInt32(ArgIndex, v) +} + +func (f ArrayValueView) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f ArrayValueView) Call() ArrayValueResults { + f.args.Mandatory(ArgIndex) + f.args.Mandatory(ArgName) + return ArrayValueResults{res: f.svc.CallView("arrayValue", &f.args)} +} + +type ArrayValueResults struct { + res wasmclient.Results +} + +func (r ArrayValueResults) Value() string { + return r.res.GetString(ResValue) +} + +///////////////////////////// blockRecord ///////////////////////////// + +type BlockRecordView struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f BlockRecordView) BlockIndex(v int32) { + f.args.SetInt32(ArgBlockIndex, v) +} + +func (f BlockRecordView) RecordIndex(v int32) { + f.args.SetInt32(ArgRecordIndex, v) +} + +func (f BlockRecordView) Call() BlockRecordResults { + f.args.Mandatory(ArgBlockIndex) + f.args.Mandatory(ArgRecordIndex) + return BlockRecordResults{res: f.svc.CallView("blockRecord", &f.args)} +} + +type BlockRecordResults struct { + res wasmclient.Results +} + +func (r BlockRecordResults) Record() []byte { + return r.res.GetBytes(ResRecord) +} + +///////////////////////////// blockRecords ///////////////////////////// + +type BlockRecordsView struct { + svc *wasmclient.Service + args wasmclient.Arguments +} + +func (f BlockRecordsView) BlockIndex(v int32) { + f.args.SetInt32(ArgBlockIndex, v) +} + +func (f BlockRecordsView) Call() BlockRecordsResults { + f.args.Mandatory(ArgBlockIndex) + return BlockRecordsResults{res: f.svc.CallView("blockRecords", &f.args)} +} + +type BlockRecordsResults struct { + res wasmclient.Results +} + +func (r BlockRecordsResults) Count() int32 { + return r.res.GetInt32(ResCount) +} + +///////////////////////////// getRandom ///////////////////////////// + +type GetRandomView struct { + svc *wasmclient.Service +} + +func (f GetRandomView) Call() GetRandomResults { + return GetRandomResults{res: f.svc.CallView("getRandom", nil)} +} + +type GetRandomResults struct { + res wasmclient.Results +} + +func (r GetRandomResults) Random() int64 { + return r.res.GetInt64(ResRandom) +} + +///////////////////////////// iotaBalance ///////////////////////////// + +type IotaBalanceView struct { + svc *wasmclient.Service +} + +func (f IotaBalanceView) Call() IotaBalanceResults { + return IotaBalanceResults{res: f.svc.CallView("iotaBalance", nil)} +} + +type IotaBalanceResults struct { + res wasmclient.Results +} + +func (r IotaBalanceResults) Iotas() int64 { + return r.res.GetInt64(ResIotas) +} + +///////////////////////////// TestWasmLibService ///////////////////////////// + +type TestWasmLibService struct { + wasmclient.Service +} + +func NewTestWasmLibService(cl *wasmclient.ServiceClient, chainID string) *TestWasmLibService { + s := &TestWasmLibService{} + s.Service.Init(cl, chainID, 0x89703a45, EventHandlers) + return s +} + +func (s *TestWasmLibService) ArrayClear() ArrayClearFunc { + return ArrayClearFunc{svc: &s.Service} +} + +func (s *TestWasmLibService) ArrayCreate() ArrayCreateFunc { + return ArrayCreateFunc{svc: &s.Service} +} + +func (s *TestWasmLibService) ArraySet() ArraySetFunc { + return ArraySetFunc{svc: &s.Service} +} + +func (s *TestWasmLibService) ParamTypes() ParamTypesFunc { + return ParamTypesFunc{svc: &s.Service} +} + +func (s *TestWasmLibService) Random() RandomFunc { + return RandomFunc{svc: &s.Service} +} + +func (s *TestWasmLibService) TriggerEvent() TriggerEventFunc { + return TriggerEventFunc{svc: &s.Service} +} + +func (s *TestWasmLibService) ArrayLength() ArrayLengthView { + return ArrayLengthView{svc: &s.Service} +} + +func (s *TestWasmLibService) ArrayValue() ArrayValueView { + return ArrayValueView{svc: &s.Service} +} + +func (s *TestWasmLibService) BlockRecord() BlockRecordView { + return BlockRecordView{svc: &s.Service} +} + +func (s *TestWasmLibService) BlockRecords() BlockRecordsView { + return BlockRecordsView{svc: &s.Service} +} + +func (s *TestWasmLibService) GetRandom() GetRandomView { + return GetRandomView{svc: &s.Service} +} + +func (s *TestWasmLibService) IotaBalance() IotaBalanceView { + return IotaBalanceView{svc: &s.Service} +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go new file mode 100644 index 0000000000..205f7ffd4d --- /dev/null +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go @@ -0,0 +1,7 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package testwasmlibclient + +func OnTestWasmLibTest(event *EventTest) { +} diff --git a/contracts/wasm/testwasmlib/schema.yaml b/contracts/wasm/testwasmlib/schema.yaml index 05b9d67cb9..1ec756a5f2 100644 --- a/contracts/wasm/testwasmlib/schema.yaml +++ b/contracts/wasm/testwasmlib/schema.yaml @@ -1,5 +1,9 @@ name: TestWasmLib description: Exercise all aspects of WasmLib +events: + test: + name: String + address: Address structs: {} typedefs: StringArray: String[] @@ -40,6 +44,10 @@ funcs: uint32: Uint32? uint64: Uint64? random: + triggerEvent: + params: + name: String + address: Address views: arrayLength: params: diff --git a/contracts/wasm/testwasmlib/src/consts.rs b/contracts/wasm/testwasmlib/src/consts.rs index 8af1763e67..9fedc8aeb7 100644 --- a/contracts/wasm/testwasmlib/src/consts.rs +++ b/contracts/wasm/testwasmlib/src/consts.rs @@ -53,6 +53,7 @@ pub const FUNC_ARRAY_CREATE : &str = "arrayCreate"; pub const FUNC_ARRAY_SET : &str = "arraySet"; pub const FUNC_PARAM_TYPES : &str = "paramTypes"; pub const FUNC_RANDOM : &str = "random"; +pub const FUNC_TRIGGER_EVENT : &str = "triggerEvent"; pub const VIEW_ARRAY_LENGTH : &str = "arrayLength"; pub const VIEW_ARRAY_VALUE : &str = "arrayValue"; pub const VIEW_BLOCK_RECORD : &str = "blockRecord"; @@ -65,6 +66,7 @@ pub const HFUNC_ARRAY_CREATE : ScHname = ScHname(0x1ed5b23b); pub const HFUNC_ARRAY_SET : ScHname = ScHname(0x2c4150b3); pub const HFUNC_PARAM_TYPES : ScHname = ScHname(0x6921c4cd); pub const HFUNC_RANDOM : ScHname = ScHname(0xe86c97ca); +pub const HFUNC_TRIGGER_EVENT : ScHname = ScHname(0xd5438ac6); pub const HVIEW_ARRAY_LENGTH : ScHname = ScHname(0x3a831021); pub const HVIEW_ARRAY_VALUE : ScHname = ScHname(0x662dbd81); pub const HVIEW_BLOCK_RECORD : ScHname = ScHname(0xad13b2f8); diff --git a/contracts/wasm/testwasmlib/src/contract.rs b/contracts/wasm/testwasmlib/src/contract.rs index 5df0ede6ee..9b7c6a7b43 100644 --- a/contracts/wasm/testwasmlib/src/contract.rs +++ b/contracts/wasm/testwasmlib/src/contract.rs @@ -39,6 +39,11 @@ pub struct RandomCall { pub func: ScFunc, } +pub struct TriggerEventCall { + pub func: ScFunc, + pub params: MutableTriggerEventParams, +} + pub struct ArrayLengthCall { pub func: ScView, pub params: MutableArrayLengthParams, @@ -119,6 +124,15 @@ impl ScFuncs { } } + pub fn trigger_event(_ctx: & dyn ScFuncCallContext) -> TriggerEventCall { + let mut f = TriggerEventCall { + func: ScFunc::new(HSC_NAME, HFUNC_TRIGGER_EVENT), + params: MutableTriggerEventParams { id: 0 }, + }; + f.func.set_ptrs(&mut f.params.id, ptr::null_mut()); + f + } + pub fn array_length(_ctx: & dyn ScViewCallContext) -> ArrayLengthCall { let mut f = ArrayLengthCall { func: ScView::new(HSC_NAME, HVIEW_ARRAY_LENGTH), diff --git a/contracts/wasm/testwasmlib/src/events.rs b/contracts/wasm/testwasmlib/src/events.rs new file mode 100644 index 0000000000..c190ba68d1 --- /dev/null +++ b/contracts/wasm/testwasmlib/src/events.rs @@ -0,0 +1,23 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +#![allow(dead_code)] + +use wasmlib::*; + +pub struct TestWasmLibEvents { +} + +impl TestWasmLibEvents { + + pub fn test(&self, address: &ScAddress, name: &str) { + let mut encoder = EventEncoder::new("testwasmlib.test"); + encoder.address(&address); + encoder.string(&name); + encoder.emit(); + } +} diff --git a/contracts/wasm/testwasmlib/src/lib.rs b/contracts/wasm/testwasmlib/src/lib.rs index f064f1deb6..84ecf72916 100644 --- a/contracts/wasm/testwasmlib/src/lib.rs +++ b/contracts/wasm/testwasmlib/src/lib.rs @@ -13,6 +13,7 @@ use wasmlib::*; use wasmlib::host::*; use crate::consts::*; +use crate::events::*; use crate::keys::*; use crate::params::*; use crate::results::*; @@ -20,6 +21,7 @@ use crate::state::*; mod consts; mod contract; +mod events; mod keys; mod params; mod results; @@ -35,6 +37,7 @@ fn on_load() { exports.add_func(FUNC_ARRAY_SET, func_array_set_thunk); exports.add_func(FUNC_PARAM_TYPES, func_param_types_thunk); exports.add_func(FUNC_RANDOM, func_random_thunk); + exports.add_func(FUNC_TRIGGER_EVENT, func_trigger_event_thunk); exports.add_view(VIEW_ARRAY_LENGTH, view_array_length_thunk); exports.add_view(VIEW_ARRAY_VALUE, view_array_value_thunk); exports.add_view(VIEW_BLOCK_RECORD, view_block_record_thunk); @@ -50,6 +53,7 @@ fn on_load() { } pub struct ArrayClearContext { + events: TestWasmLibEvents, params: ImmutableArrayClearParams, state: MutableTestWasmLibState, } @@ -57,6 +61,7 @@ pub struct ArrayClearContext { fn func_array_clear_thunk(ctx: &ScFuncContext) { ctx.log("testwasmlib.funcArrayClear"); let f = ArrayClearContext { + events: TestWasmLibEvents {}, params: ImmutableArrayClearParams { id: OBJ_ID_PARAMS, }, @@ -70,6 +75,7 @@ fn func_array_clear_thunk(ctx: &ScFuncContext) { } pub struct ArrayCreateContext { + events: TestWasmLibEvents, params: ImmutableArrayCreateParams, state: MutableTestWasmLibState, } @@ -77,6 +83,7 @@ pub struct ArrayCreateContext { fn func_array_create_thunk(ctx: &ScFuncContext) { ctx.log("testwasmlib.funcArrayCreate"); let f = ArrayCreateContext { + events: TestWasmLibEvents {}, params: ImmutableArrayCreateParams { id: OBJ_ID_PARAMS, }, @@ -90,6 +97,7 @@ fn func_array_create_thunk(ctx: &ScFuncContext) { } pub struct ArraySetContext { + events: TestWasmLibEvents, params: ImmutableArraySetParams, state: MutableTestWasmLibState, } @@ -97,6 +105,7 @@ pub struct ArraySetContext { fn func_array_set_thunk(ctx: &ScFuncContext) { ctx.log("testwasmlib.funcArraySet"); let f = ArraySetContext { + events: TestWasmLibEvents {}, params: ImmutableArraySetParams { id: OBJ_ID_PARAMS, }, @@ -112,6 +121,7 @@ fn func_array_set_thunk(ctx: &ScFuncContext) { } pub struct ParamTypesContext { + events: TestWasmLibEvents, params: ImmutableParamTypesParams, state: MutableTestWasmLibState, } @@ -119,6 +129,7 @@ pub struct ParamTypesContext { fn func_param_types_thunk(ctx: &ScFuncContext) { ctx.log("testwasmlib.funcParamTypes"); let f = ParamTypesContext { + events: TestWasmLibEvents {}, params: ImmutableParamTypesParams { id: OBJ_ID_PARAMS, }, @@ -131,12 +142,14 @@ fn func_param_types_thunk(ctx: &ScFuncContext) { } pub struct RandomContext { + events: TestWasmLibEvents, state: MutableTestWasmLibState, } fn func_random_thunk(ctx: &ScFuncContext) { ctx.log("testwasmlib.funcRandom"); let f = RandomContext { + events: TestWasmLibEvents {}, state: MutableTestWasmLibState { id: OBJ_ID_STATE, }, @@ -145,6 +158,29 @@ fn func_random_thunk(ctx: &ScFuncContext) { ctx.log("testwasmlib.funcRandom ok"); } +pub struct TriggerEventContext { + events: TestWasmLibEvents, + params: ImmutableTriggerEventParams, + state: MutableTestWasmLibState, +} + +fn func_trigger_event_thunk(ctx: &ScFuncContext) { + ctx.log("testwasmlib.funcTriggerEvent"); + let f = TriggerEventContext { + events: TestWasmLibEvents {}, + params: ImmutableTriggerEventParams { + id: OBJ_ID_PARAMS, + }, + state: MutableTestWasmLibState { + id: OBJ_ID_STATE, + }, + }; + ctx.require(f.params.address().exists(), "missing mandatory address"); + ctx.require(f.params.name().exists(), "missing mandatory name"); + func_trigger_event(ctx, &f); + ctx.log("testwasmlib.funcTriggerEvent ok"); +} + pub struct ArrayLengthContext { params: ImmutableArrayLengthParams, results: MutableArrayLengthResults, diff --git a/contracts/wasm/testwasmlib/src/params.rs b/contracts/wasm/testwasmlib/src/params.rs index aaa1f6b8b4..fa90c15fbd 100644 --- a/contracts/wasm/testwasmlib/src/params.rs +++ b/contracts/wasm/testwasmlib/src/params.rs @@ -287,6 +287,36 @@ impl MutableParamTypesParams { } } +#[derive(Clone, Copy)] +pub struct ImmutableTriggerEventParams { + pub(crate) id: i32, +} + +impl ImmutableTriggerEventParams { + pub fn address(&self) -> ScImmutableAddress { + ScImmutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + } + + pub fn name(&self) -> ScImmutableString { + ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + } +} + +#[derive(Clone, Copy)] +pub struct MutableTriggerEventParams { + pub(crate) id: i32, +} + +impl MutableTriggerEventParams { + pub fn address(&self) -> ScMutableAddress { + ScMutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + } + + pub fn name(&self) -> ScMutableString { + ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + } +} + #[derive(Clone, Copy)] pub struct ImmutableArrayLengthParams { pub(crate) id: i32, diff --git a/contracts/wasm/testwasmlib/src/testwasmlib.rs b/contracts/wasm/testwasmlib/src/testwasmlib.rs index ac32a8a541..c8f3243b87 100644 --- a/contracts/wasm/testwasmlib/src/testwasmlib.rs +++ b/contracts/wasm/testwasmlib/src/testwasmlib.rs @@ -128,3 +128,7 @@ pub fn func_random(ctx: &ScFuncContext, f: &RandomContext) { pub fn view_get_random(_ctx: &ScViewContext, f: &GetRandomContext) { f.results.random().set_value(f.state.random().value()); } + +pub fn func_trigger_event(_ctx: &ScFuncContext, f: &TriggerEventContext) { + f.events.test(&f.params.address().value(), &f.params.name().value()); +} diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm b/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm index 3d6c8e1f6e4b29230e56b0ac955fdefa55ce8fec..d97c02b6bc91945ccb1f9d9146d457f9ef257c8f 100644 GIT binary patch delta 18806 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 G$Nw*`02>bg delta 17127 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 NECWH1eDRGZ{~J3+sEhyr diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_test.go index 3cc625fccc..5b41c1b690 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_test.go @@ -9,6 +9,7 @@ import ( "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlib" "github.com/iotaledger/wasp/packages/solo" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" @@ -306,3 +307,7 @@ func TestMultiRandom(t *testing.T) { fmt.Printf("Random value: %d\n", number) } } + +func TestClient(t *testing.T) { + _ = wasmclient.ServiceClient{} +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts index a6a3b0d49c..ba2862e126 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts @@ -51,6 +51,7 @@ export const FuncArrayCreate = "arrayCreate"; export const FuncArraySet = "arraySet"; export const FuncParamTypes = "paramTypes"; export const FuncRandom = "random"; +export const FuncTriggerEvent = "triggerEvent"; export const ViewArrayLength = "arrayLength"; export const ViewArrayValue = "arrayValue"; export const ViewBlockRecord = "blockRecord"; @@ -63,6 +64,7 @@ export const HFuncArrayCreate = new wasmlib.ScHname(0x1ed5b23b); export const HFuncArraySet = new wasmlib.ScHname(0x2c4150b3); export const HFuncParamTypes = new wasmlib.ScHname(0x6921c4cd); export const HFuncRandom = new wasmlib.ScHname(0xe86c97ca); +export const HFuncTriggerEvent = new wasmlib.ScHname(0xd5438ac6); export const HViewArrayLength = new wasmlib.ScHname(0x3a831021); export const HViewArrayValue = new wasmlib.ScHname(0x662dbd81); export const HViewBlockRecord = new wasmlib.ScHname(0xad13b2f8); diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts index 7e24c61f0f..f6456b1007 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts @@ -14,6 +14,7 @@ export class ArrayClearCall { } export class ArrayClearContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableArrayClearParams = new sc.ImmutableArrayClearParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -24,6 +25,7 @@ export class ArrayCreateCall { } export class ArrayCreateContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableArrayCreateParams = new sc.ImmutableArrayCreateParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -34,6 +36,7 @@ export class ArraySetCall { } export class ArraySetContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableArraySetParams = new sc.ImmutableArraySetParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -44,6 +47,7 @@ export class ParamTypesCall { } export class ParamTypesContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableParamTypesParams = new sc.ImmutableParamTypesParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -53,6 +57,18 @@ export class RandomCall { } export class RandomContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); +} + +export class TriggerEventCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncTriggerEvent); + params: sc.MutableTriggerEventParams = new sc.MutableTriggerEventParams(); +} + +export class TriggerEventContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + params: sc.ImmutableTriggerEventParams = new sc.ImmutableTriggerEventParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -153,6 +169,12 @@ export class ScFuncs { return new RandomCall(); } + static triggerEvent(ctx: wasmlib.ScFuncCallContext): TriggerEventCall { + let f = new TriggerEventCall(); + f.func.setPtrs(f.params, null); + return f; + } + static arrayLength(ctx: wasmlib.ScViewCallContext): ArrayLengthCall { let f = new ArrayLengthCall(); f.func.setPtrs(f.params, f.results); diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/events.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/events.ts new file mode 100644 index 0000000000..8fd0c6416b --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/events.ts @@ -0,0 +1,18 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib"; + +export class TestWasmLibEvents { + + test(address: wasmlib.ScAddress, name: string): void { + new wasmlib.EventEncoder("testwasmlib.test"). + address(address). + string(name). + emit(); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/index.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/index.ts index 528a39b4da..502314c2d3 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/index.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/index.ts @@ -9,6 +9,7 @@ export * from "./testwasmlib"; export * from "./consts"; export * from "./contract"; +export * from "./events"; export * from "./keys"; export * from "./lib"; export * from "./params"; diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts index 6517abda64..d5563333a3 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts @@ -19,6 +19,7 @@ export function on_load(): void { exports.addFunc(sc.FuncArraySet, funcArraySetThunk); exports.addFunc(sc.FuncParamTypes, funcParamTypesThunk); exports.addFunc(sc.FuncRandom, funcRandomThunk); + exports.addFunc(sc.FuncTriggerEvent, funcTriggerEventThunk); exports.addView(sc.ViewArrayLength, viewArrayLengthThunk); exports.addView(sc.ViewArrayValue, viewArrayValueThunk); exports.addView(sc.ViewBlockRecord, viewBlockRecordThunk); @@ -80,6 +81,17 @@ function funcRandomThunk(ctx: wasmlib.ScFuncContext): void { ctx.log("testwasmlib.funcRandom ok"); } +function funcTriggerEventThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testwasmlib.funcTriggerEvent"); + let f = new sc.TriggerEventContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.address().exists(), "missing mandatory address"); + ctx.require(f.params.name().exists(), "missing mandatory name"); + sc.funcTriggerEvent(ctx, f); + ctx.log("testwasmlib.funcTriggerEvent ok"); +} + function viewArrayLengthThunk(ctx: wasmlib.ScViewContext): void { ctx.log("testwasmlib.viewArrayLength"); let f = new sc.ArrayLengthContext(); diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts index b4865a4e4e..ab81316512 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts @@ -244,6 +244,26 @@ export class MutableParamTypesParams extends wasmlib.ScMapID { } } +export class ImmutableTriggerEventParams extends wasmlib.ScMapID { + address(): wasmlib.ScImmutableAddress { + return new wasmlib.ScImmutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + +export class MutableTriggerEventParams extends wasmlib.ScMapID { + address(): wasmlib.ScMutableAddress { + return new wasmlib.ScMutableAddress(this.mapID, sc.idxMap[sc.IdxParamAddress]); + } + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamName]); + } +} + export class ImmutableArrayLengthParams extends wasmlib.ScMapID { name(): wasmlib.ScImmutableString { return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamName]); diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts index 1bfc31f7bd..8dcb18cc02 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts @@ -128,3 +128,7 @@ export function funcRandom(ctx: wasmlib.ScFuncContext, f: sc.RandomContext): voi export function viewGetRandom(ctx: wasmlib.ScViewContext, f: sc.GetRandomContext): void { f.results.random().setValue(f.state.random().value()); } + +export function funcTriggerEvent(ctx: wasmlib.ScFuncContext, f: sc.TriggerEventContext): void { + f.events.test(f.params.address().value(), f.params.name().value()); +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts new file mode 100644 index 0000000000..61921dd7e6 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts @@ -0,0 +1,25 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmclient" +import * as app from "./testwasmlib" + +export const eventHandlers: wasmclient.EventHandlers = { + "testwasmlib.test": onTestWasmLibTestThunk, +}; + +export class EventTest extends wasmclient.Event { + public address: wasmclient.Address; + public name: wasmclient.String; +} + +function onTestWasmLibTestThunk(message: string[]) { + let e = new EventTest(message); + e.address = e.nextAddress(); + e.name = e.nextString(); + app.onTestWasmLibTest(e); +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts new file mode 100644 index 0000000000..f3eff69c33 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts @@ -0,0 +1,10 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./testwasmlib"; +export * from "./events"; +export * from "./service"; diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts new file mode 100644 index 0000000000..f6e53308ea --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -0,0 +1,398 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmlib/wasmclient" +import * as events from "./events" + +const ArgAddress = "address"; +const ArgAgentID = "agentID"; +const ArgBlockIndex = "blockIndex"; +const ArgBool = "bool"; +const ArgBytes = "bytes"; +const ArgChainID = "chainID"; +const ArgColor = "color"; +const ArgHash = "hash"; +const ArgHname = "hname"; +const ArgIndex = "index"; +const ArgInt16 = "int16"; +const ArgInt32 = "int32"; +const ArgInt64 = "int64"; +const ArgInt8 = "int8"; +const ArgName = "name"; +const ArgParam = "this"; +const ArgRecordIndex = "recordIndex"; +const ArgRequestID = "requestID"; +const ArgString = "string"; +const ArgUint16 = "uint16"; +const ArgUint32 = "uint32"; +const ArgUint64 = "uint64"; +const ArgUint8 = "uint8"; +const ArgValue = "value"; + +const ResCount = "count"; +const ResIotas = "iotas"; +const ResLength = "length"; +const ResRandom = "random"; +const ResRecord = "record"; +const ResValue = "value"; + +///////////////////////////// arrayClear ///////////////////////////// + +export class ArrayClearFunc extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + name(v: string): void { + this.args.setString(ArgName, v); + } + + public async post(): Promise { + this.args.mandatory(ArgName); + this.svc.postRequest("arrayClear", this.args); + } +} + +///////////////////////////// arrayCreate ///////////////////////////// + +export class ArrayCreateFunc extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + name(v: string): void { + this.args.setString(ArgName, v); + } + + public async post(): Promise { + this.args.mandatory(ArgName); + this.svc.postRequest("arrayCreate", this.args); + } +} + +///////////////////////////// arraySet ///////////////////////////// + +export class ArraySetFunc extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + index(v: wasmclient.Int32): void { + this.args.setInt32(ArgIndex, v); + } + + name(v: string): void { + this.args.setString(ArgName, v); + } + + value(v: string): void { + this.args.setString(ArgValue, v); + } + + public async post(): Promise { + this.args.mandatory(ArgIndex); + this.args.mandatory(ArgName); + this.args.mandatory(ArgValue); + this.svc.postRequest("arraySet", this.args); + } +} + +///////////////////////////// paramTypes ///////////////////////////// + +export class ParamTypesFunc extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + address(v: wasmclient.Address): void { + this.args.setAddress(ArgAddress, v); + } + + agentID(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgAgentID, v); + } + + bool(v: boolean): void { + this.args.setBool(ArgBool, v); + } + + bytes(v: wasmclient.Bytes): void { + this.args.setBytes(ArgBytes, v); + } + + chainID(v: wasmclient.ChainID): void { + this.args.setChainID(ArgChainID, v); + } + + color(v: wasmclient.Color): void { + this.args.setColor(ArgColor, v); + } + + hash(v: wasmclient.Hash): void { + this.args.setHash(ArgHash, v); + } + + hname(v: wasmclient.Hname): void { + this.args.setHname(ArgHname, v); + } + + int16(v: wasmclient.Int16): void { + this.args.setInt16(ArgInt16, v); + } + + int32(v: wasmclient.Int32): void { + this.args.setInt32(ArgInt32, v); + } + + int64(v: wasmclient.Int64): void { + this.args.setInt64(ArgInt64, v); + } + + int8(v: wasmclient.Int8): void { + this.args.setInt8(ArgInt8, v); + } + + param(v: wasmclient.Bytes): void { + this.args.setBytes(ArgParam, v); + } + + requestID(v: wasmclient.RequestID): void { + this.args.setRequestID(ArgRequestID, v); + } + + string(v: string): void { + this.args.setString(ArgString, v); + } + + uint16(v: wasmclient.Uint16): void { + this.args.setUint16(ArgUint16, v); + } + + uint32(v: wasmclient.Uint32): void { + this.args.setUint32(ArgUint32, v); + } + + uint64(v: wasmclient.Uint64): void { + this.args.setUint64(ArgUint64, v); + } + + uint8(v: wasmclient.Uint8): void { + this.args.setUint8(ArgUint8, v); + } + + public async post(): Promise { + this.svc.postRequest("paramTypes", this.args); + } +} + +///////////////////////////// random ///////////////////////////// + +export class RandomFunc extends wasmclient.FuncObject { + + public async post(): Promise { + this.svc.postRequest("random", null); + } +} + +///////////////////////////// triggerEvent ///////////////////////////// + +export class TriggerEventFunc extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + address(v: wasmclient.Address): void { + this.args.setAddress(ArgAddress, v); + } + + name(v: string): void { + this.args.setString(ArgName, v); + } + + public async post(): Promise { + this.args.mandatory(ArgAddress); + this.args.mandatory(ArgName); + this.svc.postRequest("triggerEvent", this.args); + } +} + +///////////////////////////// arrayLength ///////////////////////////// + +export class ArrayLengthView extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + name(v: string): void { + this.args.setString(ArgName, v); + } + + public async call(): Promise { + this.args.mandatory(ArgName); + return new ArrayLengthResults(this.svc.callView("arrayLength", this.args)); + } +} + +export class ArrayLengthResults extends wasmclient.ViewResults { + + length(): wasmclient.Int32 { + return this.res.getInt32(ResLength); + } +} + +///////////////////////////// arrayValue ///////////////////////////// + +export class ArrayValueView extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + index(v: wasmclient.Int32): void { + this.args.setInt32(ArgIndex, v); + } + + name(v: string): void { + this.args.setString(ArgName, v); + } + + public async call(): Promise { + this.args.mandatory(ArgIndex); + this.args.mandatory(ArgName); + return new ArrayValueResults(this.svc.callView("arrayValue", this.args)); + } +} + +export class ArrayValueResults extends wasmclient.ViewResults { + + value(): string { + return this.res.getString(ResValue); + } +} + +///////////////////////////// blockRecord ///////////////////////////// + +export class BlockRecordView extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + blockIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgBlockIndex, v); + } + + recordIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgRecordIndex, v); + } + + public async call(): Promise { + this.args.mandatory(ArgBlockIndex); + this.args.mandatory(ArgRecordIndex); + return new BlockRecordResults(this.svc.callView("blockRecord", this.args)); + } +} + +export class BlockRecordResults extends wasmclient.ViewResults { + + record(): wasmclient.Bytes { + return this.res.getBytes(ResRecord); + } +} + +///////////////////////////// blockRecords ///////////////////////////// + +export class BlockRecordsView extends wasmclient.FuncObject { + args: wasmclient.Arguments = new wasmclient.Arguments(); + + blockIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgBlockIndex, v); + } + + public async call(): Promise { + this.args.mandatory(ArgBlockIndex); + return new BlockRecordsResults(this.svc.callView("blockRecords", this.args)); + } +} + +export class BlockRecordsResults extends wasmclient.ViewResults { + + count(): wasmclient.Int32 { + return this.res.getInt32(ResCount); + } +} + +///////////////////////////// getRandom ///////////////////////////// + +export class GetRandomView extends wasmclient.FuncObject { + + public async call(): Promise { + return new GetRandomResults(this.svc.callView("getRandom", null)); + } +} + +export class GetRandomResults extends wasmclient.ViewResults { + + random(): wasmclient.Int64 { + return this.res.getInt64(ResRandom); + } +} + +///////////////////////////// iotaBalance ///////////////////////////// + +export class IotaBalanceView extends wasmclient.FuncObject { + + public async call(): Promise { + return new IotaBalanceResults(this.svc.callView("iotaBalance", null)); + } +} + +export class IotaBalanceResults extends wasmclient.ViewResults { + + iotas(): wasmclient.Int64 { + return this.res.getInt64(ResIotas); + } +} + +///////////////////////////// TestWasmLibService ///////////////////////////// + +export class TestWasmLibService extends wasmclient.Service { + + constructor(cl: wasmclient.ServiceClient, chainID: string) { + super(cl, chainID, "89703a45", events.eventHandlers); + } + + public arrayClear(): ArrayClearFunc { + return new ArrayClearFunc(this); + } + + public arrayCreate(): ArrayCreateFunc { + return new ArrayCreateFunc(this); + } + + public arraySet(): ArraySetFunc { + return new ArraySetFunc(this); + } + + public paramTypes(): ParamTypesFunc { + return new ParamTypesFunc(this); + } + + public random(): RandomFunc { + return new RandomFunc(this); + } + + public triggerEvent(): TriggerEventFunc { + return new TriggerEventFunc(this); + } + + public arrayLength(): ArrayLengthView { + return new ArrayLengthView(this); + } + + public arrayValue(): ArrayValueView { + return new ArrayValueView(this); + } + + public blockRecord(): BlockRecordView { + return new BlockRecordView(this); + } + + public blockRecords(): BlockRecordsView { + return new BlockRecordsView(this); + } + + public getRandom(): GetRandomView { + return new GetRandomView(this); + } + + public iotaBalance(): IotaBalanceView { + return new IotaBalanceView(this); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts new file mode 100644 index 0000000000..ca1b553dff --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts @@ -0,0 +1,11 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as events from "./events" +import * as service from "./service" + +const client = new BasicClient(config); +const testWasmLibService = new service.TestWasmLibService(client, config.ChainId); + +export function onTestWasmLibTest(event: events.EventTest): void { +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json b/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json new file mode 100644 index 0000000000..bc17186726 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es2020"], + "target": "es2020", + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], +} diff --git a/contracts/wasm/timestamp/go/main.go b/contracts/wasm/timestamp/go/main.go index 24e856e140..1d75f182d8 100644 --- a/contracts/wasm/timestamp/go/main.go +++ b/contracts/wasm/timestamp/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/timestamp/go/timestamp" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() timestamp.OnLoad() } diff --git a/contracts/wasm/tokenregistry/go/main.go b/contracts/wasm/tokenregistry/go/main.go index e00719edd8..be160c7c43 100644 --- a/contracts/wasm/tokenregistry/go/main.go +++ b/contracts/wasm/tokenregistry/go/main.go @@ -9,7 +9,7 @@ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "github.com/iotaledger/wasp/contracts/wasm/tokenregistry/go/tokenregistry" @@ -18,7 +18,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() tokenregistry.OnLoad() } diff --git a/packages/vm/wasmlib/go/wasmlib/client/arguments.go b/packages/vm/wasmlib/go/wasmclient/arguments.go similarity index 67% rename from packages/vm/wasmlib/go/wasmlib/client/arguments.go rename to packages/vm/wasmlib/go/wasmclient/arguments.go index 1d3540c7f7..9a9fc875f6 100644 --- a/packages/vm/wasmlib/go/wasmlib/client/arguments.go +++ b/packages/vm/wasmlib/go/wasmclient/arguments.go @@ -1,24 +1,24 @@ -package client +package wasmclient import ( - "sort" "strconv" - "github.com/iotaledger/wasp/packages/kv/codec" + "github.com/iotaledger/wasp/packages/kv" + "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) // The Arguments struct is used to gather all arguments for a smart // contract function call and encode it into a deterministic byte array type Arguments struct { - args map[string][]byte + args dict.Dict } func (a Arguments) set(key string, val []byte) { if a.args == nil { - a.args = make(map[string][]byte) + a.args = make(dict.Dict) } - a.args[key] = val + a.args[kv.Key(key)] = val } func (a Arguments) setBase58(key, val string, typeID int32) { @@ -35,14 +35,14 @@ func (a Arguments) IndexedKey(key string, index int) string { func (a Arguments) Mandatory(key string) { if a.args != nil { - if _, ok := a.args[key]; ok { + if _, ok := a.args[kv.Key(key)]; ok { return } } panic("missing mandatory " + key) } -func (a Arguments) SetAddress(key string, val AgentID) { +func (a Arguments) SetAddress(key string, val Address) { a.setBase58(key, string(val), wasmlib.TYPE_ADDRESS) } @@ -74,6 +74,10 @@ func (a Arguments) SetHash(key string, val Hash) { a.setBase58(key, string(val), wasmlib.TYPE_HASH) } +func (a Arguments) SetHname(key string, val Hname) { + a.SetUint32(key, uint32(val)) +} + func (a Arguments) SetInt8(key string, val int8) { a.set(key, []byte{byte(val)}) } @@ -122,31 +126,3 @@ func (a Arguments) setUint64(key string, val uint64, size int) { } a.set(key, bytes) } - -// Encode returns a byte array that encodes the Arguments as follows: -// Sort all keys in ascending order (very important, because this data -// will be part of the data that will be signed, so the order needs to -// be 100% deterministic). Then emit a 2-byte argument count. -// Next for each argument emit a 2-byte key length, the key prepended -// with a minus sign, a 4-byte value length, and then the value bytes. -func (a Arguments) Encode() []byte { - keys := make([]string, 0, len(a.args)) - total := 2 - for k, v := range a.args { - keys = append(keys, k) - total += 2 + 1 + len(k) + 4 + len(v) - } - sort.Strings(keys) - - buf := make([]byte, 0, total) - buf = append(buf, codec.EncodeUint16(uint16(len(keys)))...) - for _, k := range keys { - buf = append(buf, codec.EncodeUint16(uint16(len(k)+1))...) - buf = append(buf, '-') - buf = append(buf, []byte(k)...) - v := a.args[k] - buf = append(buf, codec.EncodeUint32(uint32(len(v)))...) - buf = append(buf, v...) - } - return buf -} diff --git a/packages/vm/wasmlib/go/wasmlib/client/event.go b/packages/vm/wasmlib/go/wasmclient/event.go similarity index 98% rename from packages/vm/wasmlib/go/wasmlib/client/event.go rename to packages/vm/wasmlib/go/wasmclient/event.go index 491e89521f..c452801269 100644 --- a/packages/vm/wasmlib/go/wasmlib/client/event.go +++ b/packages/vm/wasmlib/go/wasmclient/event.go @@ -1,4 +1,4 @@ -package client +package wasmclient import ( "strconv" diff --git a/packages/vm/wasmlib/go/wasmclient/request.go b/packages/vm/wasmlib/go/wasmclient/request.go new file mode 100644 index 0000000000..15137992c4 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/request.go @@ -0,0 +1,14 @@ +package wasmclient + +import ( + "github.com/iotaledger/wasp/packages/iscp" +) + +type Request struct { + err error + id *iscp.RequestID +} + +func (r Request) Error() error { + return r.err +} diff --git a/packages/vm/wasmlib/go/wasmlib/client/results.go b/packages/vm/wasmlib/go/wasmclient/results.go similarity index 91% rename from packages/vm/wasmlib/go/wasmlib/client/results.go rename to packages/vm/wasmlib/go/wasmclient/results.go index 1e61d6952d..c6f6df632b 100644 --- a/packages/vm/wasmlib/go/wasmlib/client/results.go +++ b/packages/vm/wasmlib/go/wasmclient/results.go @@ -1,27 +1,30 @@ -package client +package wasmclient import ( + "github.com/iotaledger/wasp/packages/kv" + "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) // The Results struct is used to gather all arguments for a smart // contract function call and encode it into a deterministic byte array type Results struct { - res map[string][]byte + err error + res dict.Dict } -func NewResults() Results { - return Results{} +func (r Results) Error() error { + return r.err } func (r Results) Exists(key string) bool { - _, ok := r.res[key] + _, ok := r.res[kv.Key(key)] return ok } func (r Results) get(key string, typeID int32) []byte { size := wasmlib.TypeSizes[typeID] - bytes, ok := r.res[key] + bytes, ok := r.res[kv.Key(key)] if ok { if size != 0 && len(bytes) != int(size) { panic("invalid type size") diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go new file mode 100644 index 0000000000..2f8ab16288 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -0,0 +1,96 @@ +package wasmclient + +import ( + "errors" + "time" + + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/wasp/client" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/iscp/colored" + "github.com/iotaledger/wasp/packages/iscp/request" + "github.com/iotaledger/wasp/packages/iscp/requestargs" + "github.com/mr-tron/base58" +) + +type ServiceClient struct { + client *client.WaspClient + keyPair *ed25519.KeyPair +} + +type Response map[string][]byte + +type Service struct { + chainID *iscp.ChainID + cl *ServiceClient + scHname iscp.Hname +} + +func (s *Service) Init(cl *ServiceClient, chainID string, scHname uint32, eventHandlers map[string]func([]string)) (err error) { + s.scHname = iscp.Hname(scHname) + s.chainID, err = iscp.ChainIDFromString(chainID) + return err +} + +func (s *Service) CallView(viewName string, args *Arguments) (ret Results) { + ret.res, ret.err = s.cl.client.CallView(s.chainID, s.scHname, viewName, args.args) + return ret +} + +func (s *Service) PostRequest(funcHname uint32, args *Arguments, transfer ...map[string]uint64) Request { + bal, err := makeBalances(transfer...) + if err != nil { + return Request{err: err} + } + reqArgs := requestargs.New().AddEncodeSimpleMany(args.args) + req := request.NewOffLedger(s.chainID, s.scHname, iscp.Hname(funcHname), reqArgs) + req.WithTransfer(bal) + req.WithNonce(uint64(time.Now().UnixNano())) + req.Sign(s.cl.keyPair) + err = s.cl.client.PostOffLedgerRequest(s.chainID, req) + if err != nil { + return Request{err: err} + } + id := req.ID() + return Request{id: &id} +} + +func (s *Service) WaitRequest(req Request) error { + return s.cl.client.WaitUntilRequestProcessed(s.chainID, *req.id, 1*time.Minute) +} + +///////////////////////////////////////////////////////////////// + +func Base58Decode(s string) []byte { + res, err := base58.Decode(s) + if err != nil { + panic("invalid base58 encoding") + } + return res +} + +func Base58Encode(b []byte) string { + return base58.Encode(b) +} + +func makeBalances(transfer ...map[string]uint64) (colored.Balances, error) { + cb := colored.NewBalances() + switch len(transfer) { + case 0: + case 1: + for color, amount := range transfer[0] { + if color == colored.IOTA.String() { + cb.Set(colored.IOTA, amount) + continue + } + c, err := colored.ColorFromBase58EncodedString(color) + if err != nil { + return nil, err + } + cb.Set(c, amount) + } + default: + return cb, errors.New("makeBalances: too many transfers") + } + return cb, nil +} diff --git a/packages/vm/wasmlib/go/wasmlib/client/types.go b/packages/vm/wasmlib/go/wasmclient/types.go similarity index 87% rename from packages/vm/wasmlib/go/wasmlib/client/types.go rename to packages/vm/wasmlib/go/wasmclient/types.go index 8731652de8..f5aad2329c 100644 --- a/packages/vm/wasmlib/go/wasmlib/client/types.go +++ b/packages/vm/wasmlib/go/wasmclient/types.go @@ -1,4 +1,4 @@ -package client +package wasmclient type ( Address string diff --git a/packages/vm/wasmlib/go/wasmlib/client/service.go b/packages/vm/wasmlib/go/wasmlib/client/service.go deleted file mode 100644 index ea63c48800..0000000000 --- a/packages/vm/wasmlib/go/wasmlib/client/service.go +++ /dev/null @@ -1,35 +0,0 @@ -package client - -import ( - "github.com/mr-tron/base58" -) - -type ServiceClient struct{} - -type Response map[string][]byte - -type Service struct{} - -func (s *Service) Init(cl ServiceClient, chainID, scHname string, eventHandlers map[string]func([]string)) { -} - -func (s *Service) CallView(viewName string, args *Arguments) Results { - return Results{} -} - -func (s *Service) PostRequest(funcName string, args *Arguments) { -} - -///////////////////////////////////////////////////////////////// - -func Base58Decode(s string) []byte { - res, err := base58.Decode(s) - if err != nil { - panic("invalid base58 encoding") - } - return res -} - -func Base58Encode(b []byte) string { - return base58.Encode(b) -} diff --git a/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts similarity index 96% rename from packages/vm/wasmlib/ts/wasmlib/client/arguments.ts rename to packages/vm/wasmlib/ts/wasmclient/arguments.ts index dbbc38b9d7..985e567e1f 100644 --- a/packages/vm/wasmlib/ts/wasmlib/client/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -1,11 +1,11 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import * as wasmlib from "../index" +import * as wasmlib from "../wasmlib" import * as client from "./index" import {Base58} from "./crypto"; import {Buffer} from "./buffer"; -import {SimpleBufferCursor} from "../../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; +import {SimpleBufferCursor} from "../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; // The Arguments struct is used to gather all arguments for this smart // contract function call and encode it into this deterministic byte array diff --git a/packages/vm/wasmlib/ts/wasmlib/client/buffer/index.d.ts b/packages/vm/wasmlib/ts/wasmclient/buffer/index.d.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/buffer/index.d.ts rename to packages/vm/wasmlib/ts/wasmclient/buffer/index.d.ts diff --git a/packages/vm/wasmlib/ts/wasmlib/client/buffer/index.js b/packages/vm/wasmlib/ts/wasmclient/buffer/index.js similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/buffer/index.js rename to packages/vm/wasmlib/ts/wasmclient/buffer/index.js diff --git a/packages/vm/wasmlib/ts/wasmlib/client/buffer/readme.md b/packages/vm/wasmlib/ts/wasmclient/buffer/readme.md similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/buffer/readme.md rename to packages/vm/wasmlib/ts/wasmclient/buffer/readme.md diff --git a/packages/vm/wasmlib/ts/wasmlib/client/config.dev.js b/packages/vm/wasmlib/ts/wasmclient/config.dev.js similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/config.dev.js rename to packages/vm/wasmlib/ts/wasmclient/config.dev.js diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/base58.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/crypto/base58.ts rename to packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/ed25519.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/crypto/ed25519.ts rename to packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/hname.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/hname.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/crypto/hname.ts rename to packages/vm/wasmlib/ts/wasmclient/crypto/hname.ts diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/index.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/index.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/crypto/index.ts rename to packages/vm/wasmlib/ts/wasmclient/crypto/index.ts diff --git a/packages/vm/wasmlib/ts/wasmlib/client/crypto/seed.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/crypto/seed.ts rename to packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts diff --git a/packages/vm/wasmlib/ts/wasmlib/client/event.ts b/packages/vm/wasmlib/ts/wasmclient/event.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/event.ts rename to packages/vm/wasmlib/ts/wasmclient/event.ts diff --git a/packages/vm/wasmlib/ts/wasmlib/client/index.ts b/packages/vm/wasmlib/ts/wasmclient/index.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/index.ts rename to packages/vm/wasmlib/ts/wasmclient/index.ts diff --git a/packages/vm/wasmlib/ts/wasmlib/client/results.ts b/packages/vm/wasmlib/ts/wasmclient/results.ts similarity index 98% rename from packages/vm/wasmlib/ts/wasmlib/client/results.ts rename to packages/vm/wasmlib/ts/wasmclient/results.ts index cded7b816f..f6dd334a2b 100644 --- a/packages/vm/wasmlib/ts/wasmlib/client/results.ts +++ b/packages/vm/wasmlib/ts/wasmclient/results.ts @@ -1,6 +1,6 @@ // The Results struct is used to gather all arguments for a smart // contract function call and encode it into a deterministic byte array -import * as wasmlib from "../index"; +import * as wasmlib from "../wasmlib"; import * as client from "./index"; import {Base58} from "./crypto"; import {Buffer} from "./buffer"; diff --git a/packages/vm/wasmlib/ts/wasmlib/client/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts similarity index 93% rename from packages/vm/wasmlib/ts/wasmlib/client/service.ts rename to packages/vm/wasmlib/ts/wasmclient/service.ts index fb62226103..834e3a41c4 100644 --- a/packages/vm/wasmlib/ts/wasmlib/client/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -1,8 +1,8 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import * as wasp from "../../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; -import {Colors, IOffLedger, OffLedger} from "../../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; +import * as wasp from "../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; +import {Colors, IOffLedger, OffLedger} from "../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; import config from './config.dev'; import * as client from "./index" diff --git a/packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json b/packages/vm/wasmlib/ts/wasmclient/tsconfig.json similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/tsconfig.json rename to packages/vm/wasmlib/ts/wasmclient/tsconfig.json diff --git a/packages/vm/wasmlib/ts/wasmlib/client/types.ts b/packages/vm/wasmlib/ts/wasmclient/types.ts similarity index 100% rename from packages/vm/wasmlib/ts/wasmlib/client/types.ts rename to packages/vm/wasmlib/ts/wasmclient/types.ts diff --git a/packages/vm/wasmclient/host.go b/packages/vm/wasmvmhost/host.go similarity index 99% rename from packages/vm/wasmclient/host.go rename to packages/vm/wasmvmhost/host.go index aaf31ba17d..9b48bed8fd 100644 --- a/packages/vm/wasmclient/host.go +++ b/packages/vm/wasmvmhost/host.go @@ -3,7 +3,7 @@ // +build wasm -package wasmclient +package wasmvmhost import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" diff --git a/tools/schema/generator/clientbase.go b/tools/schema/generator/clientbase.go index 26745af5e6..31f7a82fde 100644 --- a/tools/schema/generator/clientbase.go +++ b/tools/schema/generator/clientbase.go @@ -12,7 +12,7 @@ type ClientBase struct { } func (g *ClientBase) Generate() error { - g.folder = g.rootFolder + "/" + g.folder = g.rootFolder + "/" + g.s.PackageName + "client/" err := os.MkdirAll(g.folder, 0o755) if err != nil { return err @@ -28,11 +28,11 @@ func (g *ClientBase) Generate() error { } func (g *ClientBase) generateCode() error { - err := g.createSourceFile("events", len(g.s.Events) != 0) + err := g.createSourceFile("events", true) if err != nil { return err } - err = g.createSourceFile("service", len(g.s.Events) != 0) + err = g.createSourceFile("service", true) if err != nil { return err } diff --git a/tools/schema/generator/goclienttemplates/alltemplates.go b/tools/schema/generator/goclienttemplates/alltemplates.go index 47e6be9a2c..351de3f721 100644 --- a/tools/schema/generator/goclienttemplates/alltemplates.go +++ b/tools/schema/generator/goclienttemplates/alltemplates.go @@ -5,12 +5,13 @@ import "github.com/iotaledger/wasp/tools/schema/model" var config = map[string]string{ "language": "Go Client", "extension": ".go", - "rootFolder": "client/go", + "rootFolder": "go", "funcRegexp": `^func On(\w+).+$`, } var Templates = []map[string]string{ config, // always first one + common, eventsGo, funcsGo, serviceGo, @@ -18,19 +19,19 @@ var Templates = []map[string]string{ var TypeDependent = model.StringMapMap{ "fldLangType": { - "Address": "client.Address", - "AgentID": "client.AgentID", + "Address": "wasmclient.Address", + "AgentID": "wasmclient.AgentID", "Bool": "bool", "Bytes": "[]byte", - "ChainID": "client.ChainID", - "Color": "client.Color", - "Hash": "client.Hash", - "Hname": "string", + "ChainID": "wasmclient.ChainID", + "Color": "wasmclient.Color", + "Hash": "wasmclient.Hash", + "Hname": "wasmclient.Hname", "Int8": "int8", "Int16": "int16", "Int32": "int32", "Int64": "int64", - "RequestID": "client.RequestID", + "RequestID": "wasmclient.RequestID", "String": "string", "Uint8": "uint8", "Uint16": "uint16", @@ -59,19 +60,19 @@ var TypeDependent = model.StringMapMap{ "": "wasmlib.TYPE_BYTES", }, "argEncode": { - "Address": "client.Base58Decode", - "AgentID": "client.Base58Decode", + "Address": "wasmclient.Base58Decode", + "AgentID": "wasmclient.Base58Decode", "Bool": "codec.EncodeBool", "Bytes": "[]byte", - "ChainID": "client.Base58Decode", - "Color": "client.Base58Decode", - "Hash": "client.Base58Decode", - "Hname": "client.Base58Decode", + "ChainID": "wasmclient.Base58Decode", + "Color": "wasmclient.Base58Decode", + "Hash": "wasmclient.Base58Decode", + "Hname": "wasmclient.Base58Decode", "Int8": "codec.EncodeInt8", "Int16": "codec.EncodeInt16", "Int32": "codec.EncodeInt32", "Int64": "codec.EncodeInt64", - "RequestID": "client.Base58Decode", + "RequestID": "wasmclient.Base58Decode", "String": "codec.EncodeString", "Uint8": "codec.EncodeUint8", "Uint16": "codec.EncodeUint16", @@ -79,19 +80,19 @@ var TypeDependent = model.StringMapMap{ "Uint64": "codec.EncodeUint64", }, "argDecode": { - "Address": "client.Base58Encode", - "AgentID": "client.Base58Encode", + "Address": "wasmclient.Base58Encode", + "AgentID": "wasmclient.Base58Encode", "Bool": "codec.DecodeBool", "Bytes": "[]byte", - "ChainID": "client.Base58Encode", - "Color": "client.Base58Encode", - "Hash": "client.Base58Encode", - "Hname": "client.Base58Encode", + "ChainID": "wasmclient.Base58Encode", + "Color": "wasmclient.Base58Encode", + "Hash": "wasmclient.Base58Encode", + "Hname": "wasmclient.Base58Encode", "Int8": "codec.DecodeInt8", "Int16": "codec.DecodeInt16", "Int32": "codec.DecodeInt32", "Int64": "codec.DecodeInt64", - "RequestID": "client.Base58Encode", + "RequestID": "wasmclient.Base58Encode", "String": "codec.DecodeString", "Uint8": "codec.DecodeUint8", "Uint16": "codec.DecodeUint16", @@ -156,3 +157,12 @@ var TypeDependent = model.StringMapMap{ "Uint64": "readBigUInt64LE(0)", }, } + +var common = map[string]string{ + // ******************************* + "clientHeader": ` +package $package$+client + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" +`, +} diff --git a/tools/schema/generator/goclienttemplates/events.go b/tools/schema/generator/goclienttemplates/events.go index 11707c5667..feabf1b3d1 100644 --- a/tools/schema/generator/goclienttemplates/events.go +++ b/tools/schema/generator/goclienttemplates/events.go @@ -3,11 +3,7 @@ package goclienttemplates var eventsGo = map[string]string{ // ******************************* "events.go": ` -package $package - -import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" -) +$#emit clientHeader var EventHandlers = map[string]func([]string) { $#each events eventHandler @@ -22,7 +18,7 @@ $#each events eventClass "eventClass": ` type Event$EvtName struct { - client.Event + wasmclient.Event $#each event eventClassField } diff --git a/tools/schema/generator/goclienttemplates/funcs.go b/tools/schema/generator/goclienttemplates/funcs.go index dd3dfd6af9..ca29c3d132 100644 --- a/tools/schema/generator/goclienttemplates/funcs.go +++ b/tools/schema/generator/goclienttemplates/funcs.go @@ -3,11 +3,7 @@ package goclienttemplates var funcsGo = map[string]string{ // ******************************* "funcs.go": ` -package $package - -import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" -) +$#emit clientHeader $#each events funcSignature `, // ******************************* diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index 973eaf7fb1..37f9e7000a 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -3,11 +3,7 @@ package goclienttemplates var serviceGo = map[string]string{ // ******************************* "service.go": ` -package $package - -import ( - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/client" -) +$#emit clientHeader const ( $#each params constArg @@ -19,12 +15,12 @@ $#each func funcStruct ///////////////////////////// $PkgName$+Service ///////////////////////////// type $PkgName$+Service struct { - client.Service + wasmclient.Service } -func New$PkgName$+Service(cl client.ServiceClient, chainID string) *$PkgName$+Service { +func New$PkgName$+Service(cl *wasmclient.ServiceClient, chainID string) *$PkgName$+Service { s := &$PkgName$+Service{} - s.Service.Init(cl, chainID, "$hscName", EventHandlers) + s.Service.Init(cl, chainID, 0x$hscName, EventHandlers) return s } $#each func serviceFunction @@ -43,7 +39,7 @@ $#each func serviceFunction ///////////////////////////// $funcName ///////////////////////////// type $FuncName$Kind struct { - svc *client.Service + svc *wasmclient.Service $#if param funcArgsMember } $#each param funcArgSetter @@ -51,7 +47,7 @@ $#if func funcPost viewCall `, // ******************************* "funcArgsMember": ` - args client.Arguments + args wasmclient.Arguments `, // ******************************* "funcArgSetter": ` @@ -77,11 +73,10 @@ func (f $FuncName$Kind) $FldName(a []$fldLangType) { // ******************************* "funcPost": ` -func (f $FuncName$Kind) Post() { +func (f $FuncName$Kind) Post(transfer ...map[string]uint64) wasmclient.Request { $#each mandatory mandatoryCheck -$#set exec f.svc.PostRequest $#if param execWithArgs execNoArgs - $exec + return f.svc.PostRequest(0x$funcHname, $args, transfer...) } `, // ******************************* @@ -89,9 +84,8 @@ $#if param execWithArgs execNoArgs func (f $FuncName$Kind) Call() $FuncName$+Results { $#each mandatory mandatoryCheck -$#set exec f.svc.CallView $#if param execWithArgs execNoArgs - return $FuncName$+Results { res: $exec } + return $FuncName$+Results { res: f.svc.CallView("$funcName", $args) } } $#if result resultStruct `, @@ -101,17 +95,17 @@ $#if result resultStruct `, // ******************************* "execWithArgs": ` -$#set exec $exec("$funcName", &f.args) +$#set args &f.args `, // ******************************* "execNoArgs": ` -$#set exec $exec("$funcName", nil) +$#set args nil `, // ******************************* "resultStruct": ` type $FuncName$+Results struct { - res client.Results + res wasmclient.Results } $#each result callResultGetter `, diff --git a/tools/schema/generator/gotemplates/main.go b/tools/schema/generator/gotemplates/main.go index e03d43c541..9a2798990b 100644 --- a/tools/schema/generator/gotemplates/main.go +++ b/tools/schema/generator/gotemplates/main.go @@ -7,7 +7,7 @@ var mainGo = map[string]string{ package main -import "github.com/iotaledger/wasp/packages/vm/wasmclient" +import "github.com/iotaledger/wasp/packages/vm/wasmvmhost" import "$module/go/$package" @@ -16,7 +16,7 @@ func main() { //export on_load func onLoad() { - h := &wasmclient.WasmVMHost{} + h := &wasmvmhost.WasmVMHost{} h.ConnectWasmHost() $package.OnLoad() } diff --git a/tools/schema/generator/tsclienttemplates/alltemplates.go b/tools/schema/generator/tsclienttemplates/alltemplates.go index 7f5470ea5e..08870a81cd 100644 --- a/tools/schema/generator/tsclienttemplates/alltemplates.go +++ b/tools/schema/generator/tsclienttemplates/alltemplates.go @@ -5,7 +5,7 @@ import "github.com/iotaledger/wasp/tools/schema/model" var config = map[string]string{ "language": "TypeScript Client", "extension": ".ts", - "rootFolder": "client/ts", + "rootFolder": "ts", "funcRegexp": `^export function on(\w+).+$`, } @@ -20,24 +20,24 @@ var Templates = []map[string]string{ var TypeDependent = model.StringMapMap{ "fldLangType": { - "Address": "client.Address", - "AgentID": "client.AgentID", + "Address": "wasmclient.Address", + "AgentID": "wasmclient.AgentID", "Bool": "boolean", - "Bytes": "client.Bytes", - "ChainID": "client.ChainID", - "Color": "client.Color", - "Hash": "client.Hash", - "Hname": "client.Hname", - "Int8": "client.Int8", - "Int16": "client.Int16", - "Int32": "client.Int32", - "Int64": "client.Int64", - "RequestID": "client.RequestID", + "Bytes": "wasmclient.Bytes", + "ChainID": "wasmclient.ChainID", + "Color": "wasmclient.Color", + "Hash": "wasmclient.Hash", + "Hname": "wasmclient.Hname", + "Int8": "wasmclient.Int8", + "Int16": "wasmclient.Int16", + "Int32": "wasmclient.Int32", + "Int64": "wasmclient.Int64", + "RequestID": "wasmclient.RequestID", "String": "string", - "Uint8": "client.Uint8", - "Uint16": "client.Uint16", - "Uint32": "client.Uint32", - "Uint64": "client.Uint64", + "Uint8": "wasmclient.Uint8", + "Uint16": "wasmclient.Uint16", + "Uint32": "wasmclient.Uint32", + "Uint64": "wasmclient.Uint64", }, "fldDefault": { "Address": "''", diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go index 84e044f0f7..eca05dc74d 100644 --- a/tools/schema/generator/tsclienttemplates/events.go +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -3,10 +3,10 @@ package tsclienttemplates var eventsTs = map[string]string{ // ******************************* "events.ts": ` -import * as client from "wasmlib/client" +import * as wasmclient from "wasmlib/wasmclient" import * as app from "./$package" -export const eventHandlers: client.EventHandlers = { +export const eventHandlers: wasmclient.EventHandlers = { $#each events eventHandler }; $#each events eventClass @@ -18,7 +18,7 @@ $#each events eventClass // ******************************* "eventClass": ` -export class Event$EvtName extends client.Event { +export class Event$EvtName extends wasmclient.Event { $#each event eventClassField } @@ -30,7 +30,7 @@ $#each event eventHandlerField `, // ******************************* "eventClassField": ` - public $fldName: client.$FldType; + public $fldName: wasmclient.$FldType; `, // ******************************* "eventHandlerField": ` diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index ff7986536c..b8ace4357c 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -3,7 +3,7 @@ package tsclienttemplates var serviceTs = map[string]string{ // ******************************* "service.ts": ` -import * as client from "wasmlib/client" +import * as wasmclient from "wasmlib/wasmclient" import * as events from "./events" $#each params constArg @@ -13,9 +13,9 @@ $#each func funcStruct ///////////////////////////// $PkgName$+Service ///////////////////////////// -export class $PkgName$+Service extends client.Service { +export class $PkgName$+Service extends wasmclient.Service { - constructor(cl: client.ServiceClient, chainID: string) { + constructor(cl: wasmclient.ServiceClient, chainID: string) { super(cl, chainID, "$hscName", events.eventHandlers); } $#each func serviceFunction @@ -34,7 +34,7 @@ const Res$FldName = "$fldAlias"; ///////////////////////////// $funcName ///////////////////////////// -export class $FuncName$Kind extends client.FuncObject { +export class $FuncName$Kind extends wasmclient.FuncObject { $#if param funcArgsMember $#each param funcArgSetter $#if func funcPost viewCall @@ -43,7 +43,7 @@ $#if result resultStruct `, // ******************************* "funcArgsMember": ` - args: client.Arguments = new client.Arguments(); + args: wasmclient.Arguments = new wasmclient.Arguments(); `, // ******************************* "funcArgSetter": ` @@ -101,7 +101,7 @@ $#set exec $exec("$funcName", null) // ******************************* "resultStruct": ` -export class $FuncName$+Results extends client.ViewResults { +export class $FuncName$+Results extends wasmclient.ViewResults { $#each result callResultGetter } `, @@ -129,7 +129,7 @@ $#if mandatory else callResultOptional `, // ******************************* "serviceResult": ` - $fldName: client.$FldType; + $fldName: wasmclient.$FldType; `, // ******************************* "serviceFunction": ` From 1349c73538b04a24786b38cbfdda184752854470 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Mon, 27 Dec 2021 13:57:51 +0200 Subject: [PATCH 019/120] Chain networking based on public keys. - NetIDs are left in the peering only. - CommitteeRecord removed, DKShare is used instead. - Access nodes are taken from the Governance contract. --- client/chain_info.go | 21 ++ client/chain_record.go | 3 + client/committee_record.go | 34 ---- client/multiclient/committee_record.go | 13 -- packages/apilib/deploychain.go | 32 ++-- packages/apilib/rundkg.go | 18 +- packages/chain/chain.go | 8 +- packages/chain/chainimpl/chainimpl.go | 82 ++++---- packages/chain/chainimpl/chainimpl_test.go | 3 + packages/chain/chainimpl/eventproc.go | 14 +- packages/chain/chainimpl/interface.go | 38 ++-- packages/chain/committee/committee.go | 60 +++--- packages/chain/committee/committee_test.go | 9 +- .../commonsubset/commonsubset_test.go | 8 +- packages/chain/consensus/setup_test.go | 17 +- packages/chain/messages/peer_block_msg.go | 3 +- packages/chain/messages/peer_get_block_msg.go | 3 +- .../messages/peer_missing_request_ids_msg.go | 3 +- .../messages/peer_offledger_request_msg.go | 3 +- .../chain/messages/peer_request_ack_msg.go | 3 +- packages/chain/statemgr/eventproc.go | 12 +- packages/chain/statemgr/setup_test.go | 43 +++-- packages/chain/statemgr/statemgr.go | 8 +- packages/chain/statemgr/statemgr_test.go | 20 +- packages/chains/chains.go | 1 - packages/dkg/messages.go | 23 +-- packages/dkg/node.go | 26 ++- packages/dkg/node_test.go | 49 +---- packages/dkg/proc.go | 38 ++-- packages/peering/domain/domain.go | 79 ++++---- packages/peering/domain/domain_test.go | 27 +-- packages/peering/group/group.go | 25 +-- packages/peering/group/group_test.go | 5 +- packages/peering/lpp/lppNetImpl.go | 70 ++++--- packages/peering/lpp/lppNetImpl_test.go | 6 +- packages/peering/lpp/lppPeer.go | 9 +- packages/peering/peer_message.go | 3 +- packages/peering/peering.go | 19 +- packages/registry/committe_record_test.go | 21 -- packages/registry/committee_record.go | 72 ------- packages/registry/providers.go | 5 - packages/registry/registry_impl.go | 23 --- packages/testutil/peeringNetBehaviour.go | 19 +- .../testutil/peeringNetBehaviourDynamic.go | 41 ++-- .../peeringNetBehaviourDynamic_test.go | 53 ++++-- packages/testutil/peeringNetBehaviour_test.go | 25 ++- packages/testutil/peeringNetworkProvider.go | 60 +++--- .../testutil/peeringNetworkProvider_test.go | 11 +- .../testutil/testchain/mock_chain_core.go | 12 ++ packages/testutil/testchain/mock_registry.go | 25 --- packages/testutil/testpeers/testkeys.go | 7 +- packages/webapi/admapi/activatechain.go | 179 +++++++++++++++++- packages/webapi/admapi/committeerecord.go | 112 ----------- packages/webapi/admapi/dkshares.go | 23 ++- packages/webapi/admapi/endpoints.go | 3 +- packages/webapi/endpoints.go | 4 + packages/webapi/model/chain_info.go | 20 ++ packages/webapi/model/committee_record.go | 22 --- packages/webapi/model/dkshares.go | 2 +- packages/webapi/request/request.go | 6 +- packages/webapi/routes/routes.go | 16 +- tools/cluster/cluster.go | 44 +++-- .../cluster/tests/advanced_inccounter_test.go | 5 +- tools/wasp-cli/chain/activate.go | 8 +- tools/wasp-cli/chain/client.go | 8 +- tools/wasp-cli/chain/committee.go | 18 -- tools/wasp-cli/chain/deploy.go | 41 ++-- tools/wasp-cli/chain/info.go | 44 ++++- 68 files changed, 834 insertions(+), 933 deletions(-) create mode 100644 client/chain_info.go delete mode 100644 client/committee_record.go delete mode 100644 client/multiclient/committee_record.go delete mode 100644 packages/registry/committe_record_test.go delete mode 100644 packages/registry/committee_record.go delete mode 100644 packages/testutil/testchain/mock_registry.go delete mode 100644 packages/webapi/admapi/committeerecord.go create mode 100644 packages/webapi/model/chain_info.go delete mode 100644 packages/webapi/model/committee_record.go delete mode 100644 tools/wasp-cli/chain/committee.go diff --git a/client/chain_info.go b/client/chain_info.go new file mode 100644 index 0000000000..f328b36ca8 --- /dev/null +++ b/client/chain_info.go @@ -0,0 +1,21 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package client + +import ( + "net/http" + + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/webapi/model" + "github.com/iotaledger/wasp/packages/webapi/routes" +) + +// GetChainRecord fetches ChainInfo by address +func (c *WaspClient) GetChainInfo(chID *iscp.ChainID) (*model.ChainInfo, error) { + res := &model.ChainInfo{} + if err := c.do(http.MethodGet, routes.GetChainInfo(chID.Base58()), nil, res); err != nil { + return nil, err + } + return res, nil +} diff --git a/client/chain_record.go b/client/chain_record.go index 8449b70f76..aaf1a3bfd2 100644 --- a/client/chain_record.go +++ b/client/chain_record.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package client import ( diff --git a/client/committee_record.go b/client/committee_record.go deleted file mode 100644 index 064f5091da..0000000000 --- a/client/committee_record.go +++ /dev/null @@ -1,34 +0,0 @@ -package client - -import ( - "net/http" - - "github.com/iotaledger/goshimmer/packages/ledgerstate" - "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/registry" - "github.com/iotaledger/wasp/packages/webapi/model" - "github.com/iotaledger/wasp/packages/webapi/routes" -) - -// PutCommitteeRecord sends a request to write a Record -func (c *WaspClient) PutCommitteeRecord(rec *registry.CommitteeRecord) error { - return c.do(http.MethodPost, routes.PutCommitteeRecord(), model.NewCommitteeRecord(rec), nil) -} - -// GetCommitteeRecord fetches a Record by address -func (c *WaspClient) GetCommitteeRecord(addr ledgerstate.Address) (*registry.CommitteeRecord, error) { - res := &model.CommitteeRecord{} - if err := c.do(http.MethodGet, routes.GetCommitteeRecord(addr.Base58()), nil, res); err != nil { - return nil, err - } - return res.Record(), nil -} - -// GetCommitteeForChain fetches the CommitteeRecord that manages the given chain -func (c *WaspClient) GetCommitteeForChain(chainID *iscp.ChainID) (*registry.CommitteeRecord, error) { - res := &model.CommitteeRecord{} - if err := c.do(http.MethodGet, routes.GetCommitteeForChain(chainID.Base58())+"?includeDeactivated=true", nil, res); err != nil { - return nil, err - } - return res.Record(), nil -} diff --git a/client/multiclient/committee_record.go b/client/multiclient/committee_record.go deleted file mode 100644 index 850c139747..0000000000 --- a/client/multiclient/committee_record.go +++ /dev/null @@ -1,13 +0,0 @@ -package multiclient - -import ( - "github.com/iotaledger/wasp/client" - "github.com/iotaledger/wasp/packages/registry" -) - -// PutChainRecord calls PutChainRecord in all wasp nodes -func (m *MultiClient) PutCommitteeRecord(bd *registry.CommitteeRecord) error { - return m.Do(func(i int, w *client.WaspClient) error { - return w.PutCommitteeRecord(bd) - }) -} diff --git a/packages/apilib/deploychain.go b/packages/apilib/deploychain.go index 2776956f6f..33d0e9fdb1 100644 --- a/packages/apilib/deploychain.go +++ b/packages/apilib/deploychain.go @@ -16,38 +16,28 @@ import ( "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/transaction" - "github.com/iotaledger/wasp/packages/util" "golang.org/x/xerrors" ) // TODO DeployChain on peering domain, not on committee type CreateChainParams struct { - Node *goshimmer.Client - AllAPIHosts []string - AllPeeringHosts []string - CommitteeAPIHosts []string - CommitteePeeringHosts []string - N uint16 - T uint16 - OriginatorKeyPair *ed25519.KeyPair - Description string - Textout io.Writer - Prefix string + Node *goshimmer.Client + CommitteeAPIHosts []string + CommitteePubKeys []string + N uint16 + T uint16 + OriginatorKeyPair *ed25519.KeyPair + Description string + Textout io.Writer + Prefix string } // DeployChainWithDKG performs all actions needed to deploy the chain // TODO: [KP] Shouldn't that be in the client packages? func DeployChainWithDKG(par CreateChainParams) (*iscp.ChainID, ledgerstate.Address, error) { - if len(par.AllPeeringHosts) > 0 { - // all committee nodes most also be among allPeers - if !util.IsSubset(par.CommitteePeeringHosts, par.AllPeeringHosts) { - return nil, nil, xerrors.Errorf("DeployChainWithDKG: committee nodes must all be among peers") - } - } - dkgInitiatorIndex := uint16(rand.Intn(len(par.CommitteeAPIHosts))) - stateControllerAddr, err := RunDKG(par.CommitteeAPIHosts, par.CommitteePeeringHosts, par.T, dkgInitiatorIndex) + stateControllerAddr, err := RunDKG(par.CommitteeAPIHosts, par.CommitteePubKeys, par.T, dkgInitiatorIndex) if err != nil { return nil, nil, err } @@ -83,7 +73,7 @@ func DeployChain(par CreateChainParams, stateControllerAddr ledgerstate.Address) fmt.Fprintf(textout, "creating chain origin and init transaction %s.. OK\n", initRequestTx.ID().Base58()) fmt.Fprint(textout, "sending committee record to nodes.. OK\n") - err = ActivateChainOnAccessNodes(par.AllAPIHosts, chainID) + err = ActivateChainOnAccessNodes(par.CommitteeAPIHosts, chainID) fmt.Fprint(textout, par.Prefix) if err != nil { fmt.Fprintf(textout, "activating chain %s.. FAILED: %v\n", chainID.Base58(), err) diff --git a/packages/apilib/rundkg.go b/packages/apilib/rundkg.go index 4a7d3cb7af..5e4d2307fc 100644 --- a/packages/apilib/rundkg.go +++ b/packages/apilib/rundkg.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package apilib import ( @@ -5,8 +8,6 @@ import ( "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/client" - "github.com/iotaledger/wasp/client/multiclient" - "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/util" "github.com/iotaledger/wasp/packages/webapi/model" "golang.org/x/xerrors" @@ -14,7 +15,7 @@ import ( // RunDKG runs DKG procedure on specific Wasp hosts: generates new keys and puts corresponding committee records // into nodes. In case of success, generated address is returned -func RunDKG(apiHosts, peeringHosts []string, threshold, initiatorIndex uint16, timeout ...time.Duration) (ledgerstate.Address, error) { +func RunDKG(apiHosts, peerPubKeys []string, threshold, initiatorIndex uint16, timeout ...time.Duration) (ledgerstate.Address, error) { to := uint32(60 * 1000) if len(timeout) > 0 { n := timeout[0].Milliseconds() @@ -26,8 +27,7 @@ func RunDKG(apiHosts, peeringHosts []string, threshold, initiatorIndex uint16, t return nil, xerrors.New("RunDKG: wrong initiator index") } dkShares, err := client.NewWaspClient(apiHosts[initiatorIndex]).DKSharesPost(&model.DKSharesPostRequest{ - PeerNetIDs: peeringHosts, - PeerPubKeys: nil, + PeerPubKeys: peerPubKeys, Threshold: threshold, TimeoutMS: to, // 1 min }) @@ -39,13 +39,5 @@ func RunDKG(apiHosts, peeringHosts []string, threshold, initiatorIndex uint16, t return nil, xerrors.Errorf("RunDKG: invalid address returned from DKG: %w", err) } - // put committee records to hosts - err = multiclient.New(apiHosts).PutCommitteeRecord(®istry.CommitteeRecord{ - Address: addr, - Nodes: peeringHosts, - }) - if err != nil { - return nil, xerrors.Errorf("RunDKG: PutCommitteeRecord: %w", err) - } return addr, nil } diff --git a/packages/chain/chain.go b/packages/chain/chain.go index db8fa5101f..7fc897ec8c 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -8,15 +8,18 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/chain/messages" "github.com/iotaledger/wasp/packages/hashing" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/iscp/coreutil" + "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/state" "github.com/iotaledger/wasp/packages/tcrypto" "github.com/iotaledger/wasp/packages/util/ready" + "github.com/iotaledger/wasp/packages/vm/core/governance" "github.com/iotaledger/wasp/packages/vm/processors" ) @@ -28,6 +31,8 @@ type ChainCore interface { Processors() *processors.Cache GlobalStateSync() coreutil.ChainStateSync GetStateReader() state.OptimisticStateReader + GetChainNodes() []peering.PeerStatusProvider // CommitteeNodes + AccessNodes + GetCandidateNodes() []*governance.AccessNodeInfo // All the current candidates. Log() *logger.Logger // Most of these methods are made public for mocking in tests @@ -81,8 +86,7 @@ type Committee interface { IsReady() bool Close() RunACSConsensus(value []byte, sessionID uint64, stateIndex uint32, callback func(sessionID uint64, acs [][]byte)) - GetOtherValidatorsPeerIDs() []string - GetRandomValidators(upToN int) []string + GetRandomValidators(upToN int) []*ed25519.PublicKey // TODO: Remove after OffLedgerRequest dissemination is changed. } type NodeConnection interface { diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index 296984008c..b8726c0954 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -29,6 +29,7 @@ import ( "github.com/iotaledger/wasp/packages/publisher" "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/state" + "github.com/iotaledger/wasp/packages/tcrypto" "github.com/iotaledger/wasp/packages/util/pipe" "github.com/iotaledger/wasp/packages/vm/core/blocklog" "github.com/iotaledger/wasp/packages/vm/core/governance" @@ -66,13 +67,13 @@ type chainObj struct { db kvstore.KVStore netProvider peering.NetworkProvider dksProvider registry.DKShareRegistryProvider - committeeRegistry registry.CommitteeRegistryProvider blobProvider registry.BlobCache eventRequestProcessed *events.Event eventChainTransition *events.Event chainPeers peering.PeerDomainProvider + candidateNodes []*governance.AccessNodeInfo offLedgerReqsAcksMutex sync.RWMutex - offLedgerReqsAcks map[iscp.RequestID][]string + offLedgerReqsAcks map[iscp.RequestID][]*ed25519.PublicKey offledgerBroadcastUpToNPeers int offledgerBroadcastInterval time.Duration pullMissingRequestsFromCommittee bool @@ -98,7 +99,6 @@ func NewChain( db kvstore.KVStore, netProvider peering.NetworkProvider, dksProvider registry.DKShareRegistryProvider, - committeeRegistry registry.CommitteeRegistryProvider, blobProvider registry.BlobCache, processorConfig *processors.Config, offledgerBroadcastUpToNPeers int, @@ -111,25 +111,25 @@ func NewChain( chainLog := log.Named(chainID.Base58()[:6] + ".") chainStateSync := coreutil.NewChainStateSync() ret := &chainObj{ - mempool: mempool.New(state.NewOptimisticStateReader(db, chainStateSync), blobProvider, chainLog, chainMetrics), - procset: processors.MustNew(processorConfig), - chainID: chainID, - log: chainLog, - nodeConn: nodeconnimpl.New(txstreamClient, chainLog), - db: db, - chainStateSync: chainStateSync, - stateReader: state.NewOptimisticStateReader(db, chainStateSync), - netProvider: netProvider, - dksProvider: dksProvider, - committeeRegistry: committeeRegistry, - blobProvider: blobProvider, + mempool: mempool.New(state.NewOptimisticStateReader(db, chainStateSync), blobProvider, chainLog, chainMetrics), + procset: processors.MustNew(processorConfig), + chainID: chainID, + log: chainLog, + nodeConn: nodeconnimpl.New(txstreamClient, chainLog), + db: db, + chainStateSync: chainStateSync, + stateReader: state.NewOptimisticStateReader(db, chainStateSync), + netProvider: netProvider, + dksProvider: dksProvider, + blobProvider: blobProvider, eventRequestProcessed: events.NewEvent(func(handler interface{}, params ...interface{}) { handler.(func(_ iscp.RequestID))(params[0].(iscp.RequestID)) }), eventChainTransition: events.NewEvent(func(handler interface{}, params ...interface{}) { handler.(func(_ *chain.ChainTransitionEventData))(params[0].(*chain.ChainTransitionEventData)) }), - offLedgerReqsAcks: make(map[iscp.RequestID][]string), + candidateNodes: make([]*governance.AccessNodeInfo, 0), + offLedgerReqsAcks: make(map[iscp.RequestID][]*ed25519.PublicKey), offledgerBroadcastUpToNPeers: offledgerBroadcastUpToNPeers, offledgerBroadcastInterval: offledgerBroadcastInterval, pullMissingRequestsFromCommittee: pullMissingRequestsFromCommittee, @@ -146,7 +146,7 @@ func NewChain( ret.eventChainTransition.Attach(events.NewClosure(ret.processChainTransition)) var err error - ret.chainPeers, err = netProvider.PeerDomain(chainID.Array(), []string{netProvider.Self().NetID()}) // TODO: PubKey. + ret.chainPeers, err = netProvider.PeerDomain(chainID.Array(), []*ed25519.PublicKey{netProvider.Self().PubKey()}) if err != nil { log.Errorf("NewChain: %v", err) return nil @@ -170,7 +170,7 @@ func (c *chainObj) receiveCommitteePeerMessages(peerMsg *peering.PeerMessageGrou } c.EnqueueMissingRequestIDsMsg(&messages.MissingRequestIDsMsgIn{ MissingRequestIDsMsg: *msg, - SenderNetID: peerMsg.SenderNetID, + SenderPubKey: peerMsg.SenderPubKey, }) } @@ -184,7 +184,7 @@ func (c *chainObj) receiveChainPeerMessages(peerMsg *peering.PeerMessageIn) { } c.EnqueueOffLedgerRequestMsg(&messages.OffLedgerRequestMsgIn{ OffLedgerRequestMsg: *msg, - SenderNetID: peerMsg.SenderNetID, + SenderPubKey: peerMsg.SenderPubKey, }) case chain.PeerMsgTypeRequestAck: msg, err := messages.NewRequestAckMsg(peerMsg.MsgData) @@ -194,7 +194,7 @@ func (c *chainObj) receiveChainPeerMessages(peerMsg *peering.PeerMessageIn) { } c.EnqueueRequestAckMsg(&messages.RequestAckMsgIn{ RequestAckMsg: *msg, - SenderNetID: peerMsg.SenderNetID, + SenderPubKey: peerMsg.SenderPubKey, }) case chain.PeerMsgTypeMissingRequest: msg, err := messages.NewMissingRequestMsg(peerMsg.MsgData) @@ -265,6 +265,7 @@ func (c *chainObj) processChainTransition(msg *chain.ChainTransitionEventData) { func (c *chainObj) updateChainNodes() { govAccessNodes := make([]ed25519.PublicKey, 0) + govCandidateNodes := make([]*governance.AccessNodeInfo, 0) if c.consensus != nil { statusSnapshot := c.consensus.GetStatusSnapshot() if statusSnapshot != nil { @@ -278,7 +279,9 @@ func (c *chainObj) updateChainNodes() { if err != nil { c.log.Panicf("unable to read the governance contract state: %v", err) } - govAccessNodes = governance.NewGetChainNodesResponseFromDict(res).AccessNodes + govResponse := governance.NewGetChainNodesResponseFromDict(res) + govAccessNodes = govResponse.AccessNodes + govCandidateNodes = govResponse.AccessNodeCandidates } } } @@ -306,6 +309,10 @@ func (c *chainObj) updateChainNodes() { newMemberList = append(newMemberList, &pubKeyCopy) } c.chainPeers.UpdatePeers(newMemberList) + + // + // Remember the candidate nodes as well (as a cache). + c.candidateNodes = govCandidateNodes } func (c *chainObj) publishNewBlockEvents(blockIndex uint32) { @@ -338,7 +345,7 @@ func (c *chainObj) rotateCommitteeIfNeeded(anchorOutput *ledgerstate.AliasOutput if !anchorOutput.GetIsGovernanceUpdated() { return xerrors.Errorf("rotateCommitteeIfNeeded: inconsistency. Governance transition expected... New output: %s", anchorOutput.String()) } - rec, err := c.getOwnCommitteeRecord(anchorOutput.GetStateAddress()) + dkShare, err := c.getChainDKShare(anchorOutput.GetStateAddress()) if err != nil { return xerrors.Errorf("rotateCommitteeIfNeeded: %w", err) } @@ -350,9 +357,9 @@ func (c *chainObj) rotateCommitteeIfNeeded(anchorOutput *ledgerstate.AliasOutput c.consensus.Close() c.setCommittee(nil) c.consensus = nil - if rec != nil { + if dkShare != nil { // create new if committee record is available - if err = c.createNewCommitteeAndConsensus(rec); err != nil { + if err = c.createNewCommitteeAndConsensus(dkShare); err != nil { return xerrors.Errorf("rotateCommitteeIfNeeded: creating committee and consensus: %v", err) } } @@ -361,28 +368,20 @@ func (c *chainObj) rotateCommitteeIfNeeded(anchorOutput *ledgerstate.AliasOutput func (c *chainObj) createCommitteeIfNeeded(anchorOutput *ledgerstate.AliasOutput) error { // check if I am in the committee - rec, err := c.getOwnCommitteeRecord(anchorOutput.GetStateAddress()) + dkShare, err := c.getChainDKShare(anchorOutput.GetStateAddress()) if err != nil { return xerrors.Errorf("rotateCommitteeIfNeeded: %w", err) } - if rec != nil { + if dkShare != nil { // create if record is present - if err = c.createNewCommitteeAndConsensus(rec); err != nil { + if err = c.createNewCommitteeAndConsensus(dkShare); err != nil { return xerrors.Errorf("rotateCommitteeIfNeeded: creating committee and consensus: %v", err) } } return nil } -func (c *chainObj) getOwnCommitteeRecord(addr ledgerstate.Address) (*registry.CommitteeRecord, error) { - rec, err := c.committeeRegistry.GetCommitteeRecord(addr) - if err != nil { - return nil, xerrors.Errorf("createCommitteeIfNeeded: reading committee record: %v", err) - } - if rec == nil { - // committee record wasn't found in th registry, I am not the part of the committee - return nil, nil - } +func (c *chainObj) getChainDKShare(addr ledgerstate.Address) (*tcrypto.DKShare, error) { // // just in case check if I am among committee nodes // should not happen @@ -401,29 +400,28 @@ func (c *chainObj) getOwnCommitteeRecord(addr ledgerstate.Address) (*registry.Co if !found { return nil, xerrors.Errorf("createCommitteeIfNeeded: I am not among nodes of the committee record. Inconsistency") } - return rec, nil + return cmtDKShare, nil } -func (c *chainObj) createNewCommitteeAndConsensus(cmtRec *registry.CommitteeRecord) error { +func (c *chainObj) createNewCommitteeAndConsensus(dkShare *tcrypto.DKShare) error { c.log.Debugf("createNewCommitteeAndConsensus: creating a new committee...") cmt, cmtPeerGroup, err := committee.New( - cmtRec, + dkShare, c.chainID, c.netProvider, - c.dksProvider, c.log, ) if err != nil { c.setCommittee(nil) return xerrors.Errorf("createNewCommitteeAndConsensus: failed to create committee object for state address %s: %w", - cmtRec.Address.Base58(), err) + dkShare.Address.Base58(), err) } cmtPeerGroup.Attach(peering.PeerMessageReceiverChain, c.receiveCommitteePeerMessages) c.log.Debugf("creating new consensus object...") c.consensus = consensus.New(c, c.mempool, cmt, cmtPeerGroup, c.nodeConn, c.pullMissingRequestsFromCommittee, c.chainMetrics) c.setCommittee(cmt) - c.log.Infof("NEW COMMITTEE OF VALIDATORS has been initialized for the state address %s", cmtRec.Address.Base58()) + c.log.Infof("NEW COMMITTEE OF VALIDATORS has been initialized for the state address %s", dkShare.Address.Base58()) return nil } diff --git a/packages/chain/chainimpl/chainimpl_test.go b/packages/chain/chainimpl/chainimpl_test.go index 8670c53fe9..0273e02427 100644 --- a/packages/chain/chainimpl/chainimpl_test.go +++ b/packages/chain/chainimpl/chainimpl_test.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package chainimpl import ( diff --git a/packages/chain/chainimpl/eventproc.go b/packages/chain/chainimpl/eventproc.go index 5e848b356b..3336249c8e 100644 --- a/packages/chain/chainimpl/eventproc.go +++ b/packages/chain/chainimpl/eventproc.go @@ -130,8 +130,8 @@ func (c *chainObj) EnqueueOffLedgerRequestMsg(msg *messages.OffLedgerRequestMsgI } func (c *chainObj) handleOffLedgerRequestMsg(msg *messages.OffLedgerRequestMsgIn) { - c.log.Debugf("handleOffLedgerRequestMsg message received from peer %v, reqID: %s", msg.SenderNetID, msg.Req.ID().Base58()) - c.sendRequestAcknowledgementMsg(msg.Req.ID(), msg.SenderNetID) + c.log.Debugf("handleOffLedgerRequestMsg message received from peer %v, reqID: %s", msg.SenderPubKey.String(), msg.Req.ID().Base58()) + c.sendRequestAcknowledgementMsg(msg.Req.ID(), msg.SenderPubKey) if !c.isRequestValid(msg.Req) { // this means some node broadcasted an invalid request (bad chainID or signature) @@ -157,10 +157,10 @@ func (c *chainObj) EnqueueRequestAckMsg(msg *messages.RequestAckMsgIn) { } func (c *chainObj) handleRequestAckPeerMsg(msg *messages.RequestAckMsgIn) { - c.log.Debugf("handleRequestAckPeerMsg message received from peer %v, reqID: %s", msg.SenderNetID, msg.ReqID.Base58()) + c.log.Debugf("handleRequestAckPeerMsg message received from peer %v, reqID: %s", msg.SenderPubKey.String(), msg.ReqID.Base58()) c.offLedgerReqsAcksMutex.Lock() defer c.offLedgerReqsAcksMutex.Unlock() - c.offLedgerReqsAcks[*msg.ReqID] = append(c.offLedgerReqsAcks[*msg.ReqID], msg.SenderNetID) + c.offLedgerReqsAcks[*msg.ReqID] = append(c.offLedgerReqsAcks[*msg.ReqID], msg.SenderPubKey) c.chainMetrics.CountRequestAckMessages() c.log.Debugf("handleRequestAckPeerMsg comleted: reqID: %s", msg.ReqID.Base58()) } @@ -171,7 +171,7 @@ func (c *chainObj) EnqueueMissingRequestIDsMsg(msg *messages.MissingRequestIDsMs } func (c *chainObj) handleMissingRequestIDsMsg(msg *messages.MissingRequestIDsMsgIn) { - c.log.Debugf("handleMissingRequestIDsMsg message received from peer %v, number of reqIDs: %v", msg.SenderNetID, len(msg.IDs)) + c.log.Debugf("handleMissingRequestIDsMsg message received from peer %v, number of reqIDs: %v", msg.SenderPubKey.String(), len(msg.IDs)) if !c.pullMissingRequestsFromCommittee { c.log.Warnf("handleMissingRequestIDsMsg ignored: pull from committee disabled") return @@ -180,8 +180,8 @@ func (c *chainObj) handleMissingRequestIDsMsg(msg *messages.MissingRequestIDsMsg c.log.Debugf("handleMissingRequestIDsMsg: finding reqID %s...", reqID.Base58()) if req := c.mempool.GetRequest(reqID); req != nil { resultMsg := &messages.MissingRequestMsg{Request: req} - c.chainPeers.SendMsgByNetID(msg.SenderNetID, peering.PeerMessageReceiverChain, chain.PeerMsgTypeMissingRequest, resultMsg.Bytes()) - c.log.Warnf("handleMissingRequestIDsMsg: reqID %s sent to %v.", reqID.Base58(), msg.SenderNetID) + c.chainPeers.SendMsgByPubKey(msg.SenderPubKey, peering.PeerMessageReceiverChain, chain.PeerMsgTypeMissingRequest, resultMsg.Bytes()) + c.log.Warnf("handleMissingRequestIDsMsg: reqID %s sent to %v.", reqID.Base58(), msg.SenderPubKey.String()) } else { c.log.Warnf("handleMissingRequestIDsMsg: reqID %s not found.", reqID.Base58()) } diff --git a/packages/chain/chainimpl/interface.go b/packages/chain/chainimpl/interface.go index 7f528440cf..eb86d58c90 100644 --- a/packages/chain/chainimpl/interface.go +++ b/packages/chain/chainimpl/interface.go @@ -7,6 +7,7 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/chain" @@ -20,6 +21,7 @@ import ( "github.com/iotaledger/wasp/packages/state" "github.com/iotaledger/wasp/packages/transaction" "github.com/iotaledger/wasp/packages/vm/core/blocklog" + "github.com/iotaledger/wasp/packages/vm/core/governance" "github.com/iotaledger/wasp/packages/vm/processors" ) @@ -97,9 +99,9 @@ func (c *chainObj) StateCandidateToStateManager(virtualState state.VirtualStateA c.stateMgr.EnqueueStateCandidateMsg(virtualState, outputID) } -func shouldSendToPeer(peerID string, ackPeers []string) bool { +func shouldSendToPeer(peerPubKey *ed25519.PublicKey, ackPeers []*ed25519.PublicKey) bool { for _, p := range ackPeers { - if p == peerID { + if *p == *peerPubKey { return false } } @@ -113,18 +115,18 @@ func (c *chainObj) broadcastOffLedgerRequest(req *request.OffLedger) { Req: req, } committee := c.getCommittee() - getPeerIDs := c.chainPeers.GetRandomPeers + getPeerPubKeys := c.chainPeers.GetRandomPeers if committee != nil { - getPeerIDs = committee.GetRandomValidators + getPeerPubKeys = committee.GetRandomValidators } - sendMessage := func(ackPeers []string) { - peerIDs := getPeerIDs(c.offledgerBroadcastUpToNPeers) - for _, peerID := range peerIDs { - if shouldSendToPeer(peerID, ackPeers) { - c.log.Debugf("sending offledger request ID: reqID: %s, peerID: %s", req.ID().Base58(), peerID) - c.chainPeers.SendMsgByNetID(peerID, peering.PeerMessageReceiverChain, chain.PeerMsgTypeOffLedgerRequest, msg.Bytes()) + sendMessage := func(ackPeers []*ed25519.PublicKey) { + peerPubKeys := getPeerPubKeys(c.offledgerBroadcastUpToNPeers) + for _, peerPubKey := range peerPubKeys { + if shouldSendToPeer(peerPubKey, ackPeers) { + c.log.Debugf("sending offledger request ID: reqID: %s, peerPubKey: %s", req.ID().Base58(), peerPubKey.String()) + c.chainPeers.SendMsgByPubKey(peerPubKey, peering.PeerMessageReceiverChain, chain.PeerMsgTypeOffLedgerRequest, msg.Bytes()) } } } @@ -157,13 +159,13 @@ func (c *chainObj) broadcastOffLedgerRequest(req *request.OffLedger) { }() } -func (c *chainObj) sendRequestAcknowledgementMsg(reqID iscp.RequestID, peerID string) { - c.log.Debugf("sendRequestAcknowledgementMsg: reqID: %s, peerID: %s", reqID.Base58(), peerID) - if peerID == "" { +func (c *chainObj) sendRequestAcknowledgementMsg(reqID iscp.RequestID, peerPubKey *ed25519.PublicKey) { + if peerPubKey == nil { return } + c.log.Debugf("sendRequestAcknowledgementMsg: reqID: %s, peerID: %s", reqID.Base58(), peerPubKey.String()) msg := &messages.RequestAckMsg{ReqID: &reqID} - c.chainPeers.SendMsgByNetID(peerID, peering.PeerMessageReceiverChain, chain.PeerMsgTypeRequestAck, msg.Bytes()) + c.chainPeers.SendMsgByPubKey(peerPubKey, peering.PeerMessageReceiverChain, chain.PeerMsgTypeRequestAck, msg.Bytes()) } func (c *chainObj) ReceiveTransaction(tx *ledgerstate.Transaction) { @@ -243,6 +245,14 @@ func (c *chainObj) GetStateReader() state.OptimisticStateReader { return state.NewOptimisticStateReader(c.db, c.chainStateSync) } +func (c *chainObj) GetChainNodes() []peering.PeerStatusProvider { + return c.chainPeers.PeerStatus() +} + +func (c *chainObj) GetCandidateNodes() []*governance.AccessNodeInfo { + return c.candidateNodes +} + func (c *chainObj) Log() *logger.Logger { return c.log } diff --git a/packages/chain/committee/committee.go b/packages/chain/committee/committee.go index aed36fef8d..8932865e0d 100644 --- a/packages/chain/committee/committee.go +++ b/packages/chain/committee/committee.go @@ -8,12 +8,12 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/chain" "github.com/iotaledger/wasp/packages/chain/consensus/commonsubset" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/peering" - "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/tcrypto" "github.com/iotaledger/wasp/packages/util" "go.uber.org/atomic" @@ -37,24 +37,19 @@ var _ chain.Committee = &committee{} const waitReady = false func New( - cmtRec *registry.CommitteeRecord, + dkShare *tcrypto.DKShare, chainID *iscp.ChainID, netProvider peering.NetworkProvider, - dksProvider registry.DKShareRegistryProvider, log *logger.Logger, acsRunner ...chain.AsynchronousCommonSubsetRunner, // Only for mocking. ) (chain.Committee, peering.GroupProvider, error) { - // load DKShare from the registry - dkshare, err := dksProvider.LoadDKShare(cmtRec.Address) - if err != nil { - return nil, nil, xerrors.Errorf("NewCommittee: failed loading DKShare for address %s: %w", cmtRec.Address.Base58(), err) - } - if dkshare.Index == nil { - return nil, nil, xerrors.Errorf("NewCommittee: wrong DKShare record for address %s: %w", cmtRec.Address.Base58(), err) + var err error + if dkShare.Index == nil { + return nil, nil, xerrors.Errorf("NewCommittee: wrong DKShare record for address %s: nil index", dkShare.Address.Base58()) } // peerGroupID is calculated by XORing chainID and stateAddr. // It allows to use same statAddr for different chains - peerGroupID := cmtRec.Address.Array() + peerGroupID := dkShare.Address.Array() var chainArr [33]byte if chainID != nil { chainArr = chainID.Array() @@ -63,18 +58,18 @@ func New( peerGroupID[i] ^= chainArr[i] } var peers peering.GroupProvider - if peers, err = netProvider.PeerGroup(peerGroupID, cmtRec.Nodes); err != nil { - return nil, nil, xerrors.Errorf("NewCommittee: failed to create peer group for committee: %+v: %w", cmtRec.Nodes, err) + if peers, err = netProvider.PeerGroup(peerGroupID, dkShare.NodePubKeys); err != nil { + return nil, nil, xerrors.Errorf("NewCommittee: failed to create peer group for committee: %+v: %w", dkShare.NodePubKeys, err) } - log.Debugf("NewCommittee: peer group: %+v", cmtRec.Nodes) + log.Debugf("NewCommittee: peer group: %+v", dkShare.NodePubKeys) ret := &committee{ isReady: atomic.NewBool(false), - address: cmtRec.Address, + address: dkShare.Address, validatorNodes: peers, - size: dkshare.N, - quorum: dkshare.T, - ownIndex: *dkshare.Index, - dkshare: dkshare, + size: dkShare.N, + quorum: dkShare.T, + ownIndex: *dkShare.Index, + dkshare: dkShare, log: log, } if len(acsRunner) > 0 { @@ -85,7 +80,7 @@ func New( ret.acsRunner = commonsubset.NewCommonSubsetCoordinator( netProvider, ret.validatorNodes, - dkshare, + dkShare, log, ) } @@ -184,21 +179,14 @@ func (c *committee) waitReady(waitReady bool) { c.isReady.Store(true) } -func (c *committee) GetOtherValidatorsPeerIDs() []string { - nodes := c.validatorNodes.OtherNodes() - ret := make([]string, len(nodes)) - i := 0 - for _, node := range nodes { - ret[i] = node.NetID() - i++ - } - return ret -} - -func (c *committee) GetRandomValidators(upToN int) []string { - validators := c.GetOtherValidatorsPeerIDs() +func (c *committee) GetRandomValidators(upToN int) []*ed25519.PublicKey { + validators := c.validatorNodes.OtherNodes() if upToN >= len(validators) { - return validators + valPubKeys := make([]*ed25519.PublicKey, 0) + for i := range validators { + valPubKeys = append(valPubKeys, validators[i].PubKey()) + } + return valPubKeys } var b [8]byte @@ -206,10 +194,10 @@ func (c *committee) GetRandomValidators(upToN int) []string { _, _ = rand.Read(seed) permutation := util.NewPermutation16(uint16(len(validators)), seed) permutation.Shuffle(seed) - ret := make([]string, 0) + ret := make([]*ed25519.PublicKey, 0) for len(ret) < upToN { i := permutation.Next() - ret = append(ret, validators[i]) + ret = append(ret, validators[i].PubKey()) } return ret diff --git a/packages/chain/committee/committee_test.go b/packages/chain/committee/committee_test.go index 10f4b280e0..3839cbdb66 100644 --- a/packages/chain/committee/committee_test.go +++ b/packages/chain/committee/committee_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/tcrypto" "github.com/iotaledger/wasp/packages/testutil" "github.com/iotaledger/wasp/packages/testutil/testlogger" @@ -24,12 +23,10 @@ func TestCommitteeBasic(t *testing.T) { stateAddr, dksRegistries := testpeers.SetupDkgPregenerated(t, uint16((len(netIDs)*2)/3+1), identities, suite) nodes, netCloser := testpeers.SetupNet(netIDs, identities, testutil.NewPeeringNetReliable(log), log) net0 := nodes[0] + dks0, err := dksRegistries[0].LoadDKShare(stateAddr) + require.NoError(t, err) - cmtRec := ®istry.CommitteeRecord{ - Address: stateAddr, - Nodes: netIDs, - } - c, _, err := New(cmtRec, nil, net0, dksRegistries[0], log) + c, _, err := New(dks0, nil, net0, log) require.NoError(t, err) require.True(t, c.Address().Equals(stateAddr)) require.EqualValues(t, 4, c.Size()) diff --git a/packages/chain/consensus/commonsubset/commonsubset_test.go b/packages/chain/consensus/commonsubset/commonsubset_test.go index 8fe66e6977..eef363d172 100644 --- a/packages/chain/consensus/commonsubset/commonsubset_test.go +++ b/packages/chain/consensus/commonsubset/commonsubset_test.go @@ -61,7 +61,7 @@ func testBasic(t *testing.T, peerCount, threshold uint16, allRandom bool) { acsPeers := make([]*CommonSubset, peerCount) for a := range acsPeers { - group, err := networkProviders[a].PeerGroup(peeringID, peerNetIDs) + group, err := networkProviders[a].PeerGroup(peeringID, testpeers.PublicKeys(peerIdentities)) require.Nil(t, err) acsLog := testlogger.WithLevel(log.Named(fmt.Sprintf("ACS[%02d]", a)), logger.LevelInfo, false) acsPeers[a], err = NewCommonSubset(0, 0, group, dkShares[a], allRandom, nil, acsLog) @@ -110,7 +110,7 @@ func TestRandomized(t *testing.T) { acsPeers := make([]*CommonSubset, peerCount) for a := range acsPeers { - group, err := networkProviders[a].PeerGroup(peeringID, peerNetIDs) + group, err := networkProviders[a].PeerGroup(peeringID, testpeers.PublicKeys(peerIdentities)) require.Nil(t, err) acsLog := testlogger.WithLevel(log.Named(fmt.Sprintf("ACS[%02d]", a)), logger.LevelInfo, false) acsPeers[a], err = NewCommonSubset(0, 0, group, dkShares[a], true, nil, acsLog) @@ -205,7 +205,7 @@ func testCoordinator(t *testing.T, peerCount, threshold uint16, inputLen int) { acsCoords := make([]*CommonSubsetCoordinator, peerCount) for i := range acsCoords { - group, err := networkProviders[i].PeerGroup(peeringID, peerNetIDs) + group, err := networkProviders[i].PeerGroup(peeringID, testpeers.PublicKeys(peerIdentities)) require.Nil(t, err) acsLog := testlogger.WithLevel(log.Named(fmt.Sprintf("CSC[%02d]", i)), logger.LevelInfo, false) acsCoords[i] = NewCommonSubsetCoordinator(networkProviders[i], group, dkShares[i], acsLog) @@ -267,7 +267,7 @@ func testRandomizedWithCC(t *testing.T, peerCount, threshold uint16, inputLen in dkAddress, dkShares := testpeers.SetupDkgPregenerated(t, threshold, peerIdentities, tcrypto.DefaultSuite()) acsCoords := make([]*CommonSubsetCoordinator, peerCount) for i := range acsCoords { - group, err := networkProviders[i].PeerGroup(peeringID, peerNetIDs) + group, err := networkProviders[i].PeerGroup(peeringID, testpeers.PublicKeys(peerIdentities)) require.Nil(t, err) dkShare, err := dkShares[i].LoadDKShare(dkAddress) require.Nil(t, err) diff --git a/packages/chain/consensus/setup_test.go b/packages/chain/consensus/setup_test.go index 2a3a6450aa..2ade83f5c7 100644 --- a/packages/chain/consensus/setup_test.go +++ b/packages/chain/consensus/setup_test.go @@ -114,10 +114,10 @@ func newMockedEnv(t *testing.T, n, quorum uint16, debug, mockACS bool) (*MockedE ret.NetworkBehaviour = testutil.NewPeeringNetDynamic(log) log.Infof("running DKG and setting up mocked network..") - nodeIDs, identities := testpeers.SetupKeys(n) + nodeIDs, nodeIdentities := testpeers.SetupKeys(n) ret.NodeIDs = nodeIDs - ret.StateAddress, ret.DKSRegistries = testpeers.SetupDkgPregenerated(t, quorum, identities, tcrypto.DefaultSuite()) - ret.NetworkProviders, ret.NetworkCloser = testpeers.SetupNet(ret.NodeIDs, identities, ret.NetworkBehaviour, log) + ret.StateAddress, ret.DKSRegistries = testpeers.SetupDkgPregenerated(t, quorum, nodeIdentities, tcrypto.DefaultSuite()) + ret.NetworkProviders, ret.NetworkCloser = testpeers.SetupNet(ret.NodeIDs, nodeIdentities, ret.NetworkBehaviour, log) ret.OriginatorKeyPair, ret.OriginatorAddress = ret.Ledger.NewKeyPairByIndex(0) _, err = ret.Ledger.RequestFunds(ret.OriginatorAddress) @@ -209,21 +209,20 @@ func (env *MockedEnv) NewNode(nodeIndex uint16, timers ConsensusTimers) *mockedN if env.MockedACS != nil { acs = append(acs, env.MockedACS) } - cmtRec := ®istry.CommitteeRecord{ - Address: env.StateAddress, - Nodes: env.NodeIDs, + dkShare, err := env.DKSRegistries[nodeIndex].LoadDKShare(env.StateAddress) + if err != nil { + panic(err) } cmt, cmtPeerGroup, err := committee.New( - cmtRec, + dkShare, env.ChainID, env.NetworkProviders[nodeIndex], - env.DKSRegistries[nodeIndex], log, acs..., ) require.NoError(env.T, err) cmtPeerGroup.Attach(peering.PeerMessageReceiverConsensus, func(peerMsg *peering.PeerMessageGroupIn) { - log.Debugf("Consensus received peer message from %v of type %v", peerMsg.SenderNetID, peerMsg.MsgType) + log.Debugf("Consensus received peer message from %v of type %v", peerMsg.SenderPubKey.String(), peerMsg.MsgType) switch peerMsg.MsgType { case peerMsgTypeSignedResult: msg, err := messages.NewSignedResultMsg(peerMsg.MsgData) diff --git a/packages/chain/messages/peer_block_msg.go b/packages/chain/messages/peer_block_msg.go index 452b0bdc34..e166565a76 100644 --- a/packages/chain/messages/peer_block_msg.go +++ b/packages/chain/messages/peer_block_msg.go @@ -7,6 +7,7 @@ import ( "bytes" "io" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/packages/util" ) @@ -18,7 +19,7 @@ type BlockMsg struct { type BlockMsgIn struct { BlockMsg - SenderNetID string + SenderPubKey *ed25519.PublicKey } func NewBlockMsg(data []byte) (*BlockMsg, error) { diff --git a/packages/chain/messages/peer_get_block_msg.go b/packages/chain/messages/peer_get_block_msg.go index 48d4a500c6..21aec929d2 100644 --- a/packages/chain/messages/peer_get_block_msg.go +++ b/packages/chain/messages/peer_get_block_msg.go @@ -7,6 +7,7 @@ import ( "bytes" "io" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/packages/util" ) @@ -18,7 +19,7 @@ type GetBlockMsg struct { type GetBlockMsgIn struct { GetBlockMsg - SenderNetID string + SenderPubKey *ed25519.PublicKey } func NewGetBlockMsg(data []byte) (*GetBlockMsg, error) { diff --git a/packages/chain/messages/peer_missing_request_ids_msg.go b/packages/chain/messages/peer_missing_request_ids_msg.go index ed32285b4e..265eef4dba 100644 --- a/packages/chain/messages/peer_missing_request_ids_msg.go +++ b/packages/chain/messages/peer_missing_request_ids_msg.go @@ -4,6 +4,7 @@ package messages import ( + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/wasp/packages/iscp" ) @@ -14,7 +15,7 @@ type MissingRequestIDsMsg struct { type MissingRequestIDsMsgIn struct { MissingRequestIDsMsg - SenderNetID string + SenderPubKey *ed25519.PublicKey } func (msg *MissingRequestIDsMsg) Bytes() []byte { diff --git a/packages/chain/messages/peer_offledger_request_msg.go b/packages/chain/messages/peer_offledger_request_msg.go index 672a7e3414..ab1b3f965d 100644 --- a/packages/chain/messages/peer_offledger_request_msg.go +++ b/packages/chain/messages/peer_offledger_request_msg.go @@ -4,6 +4,7 @@ package messages import ( + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/iscp/request" @@ -17,7 +18,7 @@ type OffLedgerRequestMsg struct { type OffLedgerRequestMsgIn struct { OffLedgerRequestMsg - SenderNetID string + SenderPubKey *ed25519.PublicKey } func NewOffLedgerRequestMsg(data []byte) (*OffLedgerRequestMsg, error) { diff --git a/packages/chain/messages/peer_request_ack_msg.go b/packages/chain/messages/peer_request_ack_msg.go index 35bbbba2a2..179ec8f2ab 100644 --- a/packages/chain/messages/peer_request_ack_msg.go +++ b/packages/chain/messages/peer_request_ack_msg.go @@ -7,6 +7,7 @@ import ( "bytes" "io" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/packages/iscp" "golang.org/x/xerrors" ) @@ -17,7 +18,7 @@ type RequestAckMsg struct { type RequestAckMsgIn struct { RequestAckMsg - SenderNetID string + SenderPubKey *ed25519.PublicKey } func NewRequestAckMsg(buf []byte) (*RequestAckMsg, error) { diff --git a/packages/chain/statemgr/eventproc.go b/packages/chain/statemgr/eventproc.go index c3a5a4c2cf..8c30f49feb 100644 --- a/packages/chain/statemgr/eventproc.go +++ b/packages/chain/statemgr/eventproc.go @@ -20,7 +20,7 @@ func (sm *stateManager) EnqueueGetBlockMsg(msg *messages.GetBlockMsgIn) { func (sm *stateManager) handleGetBlockMsg(msg *messages.GetBlockMsgIn) { sm.log.Debugw("handleGetBlockMsg: ", - "sender", msg.SenderNetID, + "sender", msg.SenderPubKey.String(), "block index", msg.BlockIndex, ) if sm.stateOutput == nil { // Not a necessary check, only for optimization. @@ -43,10 +43,10 @@ func (sm *stateManager) handleGetBlockMsg(msg *messages.GetBlockMsgIn) { return } - sm.log.Debugf("handleGetBlockMsg: responding to peer %s by block %v", msg.SenderNetID, msg.BlockIndex) + sm.log.Debugf("handleGetBlockMsg: responding to peer %s by block %v", msg.SenderPubKey.String(), msg.BlockIndex) blockMsg := &messages.BlockMsg{BlockBytes: blockBytes} - sm.chainPeers.SendMsgByNetID(msg.SenderNetID, peering.PeerMessageReceiverStateManager, peerMsgTypeBlock, util.MustBytes(blockMsg)) + sm.chainPeers.SendMsgByPubKey(msg.SenderPubKey, peering.PeerMessageReceiverStateManager, peerMsgTypeBlock, util.MustBytes(blockMsg)) } // EventBlockMsg @@ -56,7 +56,7 @@ func (sm *stateManager) EnqueueBlockMsg(msg *messages.BlockMsgIn) { func (sm *stateManager) handleBlockMsg(msg *messages.BlockMsgIn) { sm.log.Debugw("handleBlockMsg: ", - "sender", msg.SenderNetID, + "sender", msg.SenderPubKey.String(), ) if sm.stateOutput == nil { sm.log.Debugf("handleBlockMsg: message ignored: stateOutput is nil") @@ -64,11 +64,11 @@ func (sm *stateManager) handleBlockMsg(msg *messages.BlockMsgIn) { } block, err := state.BlockFromBytes(msg.BlockBytes) if err != nil { - sm.log.Warnf("handleBlockMsg: message ignored: wrong block received from peer %s. Err: %v", msg.SenderNetID, err) + sm.log.Warnf("handleBlockMsg: message ignored: wrong block received from peer %s. Err: %v", msg.SenderPubKey.String(), err) return } sm.log.Debugw("handleBlockMsg: adding block from peer ", - "sender", msg.SenderNetID, + "sender", msg.SenderPubKey.String(), "block index", block.BlockIndex(), "approving output", iscp.OID(block.ApprovingOutputID()), ) diff --git a/packages/chain/statemgr/setup_test.go b/packages/chain/statemgr/setup_test.go index e39d5712d1..e9056e531e 100644 --- a/packages/chain/statemgr/setup_test.go +++ b/packages/chain/statemgr/setup_test.go @@ -39,18 +39,18 @@ type MockedEnv struct { Ledger *utxodb.UtxoDB OriginatorKeyPair *ed25519.KeyPair OriginatorAddress ledgerstate.Address - NodeIDs []string + NodePubKeys []*ed25519.PublicKey NetworkProviders []peering.NetworkProvider NetworkBehaviour *testutil.PeeringNetDynamic NetworkCloser io.Closer ChainID *iscp.ChainID mutex sync.Mutex - Nodes map[string]*MockedNode + Nodes map[ed25519.PublicKey]*MockedNode push bool } type MockedNode struct { - NetID string + PubKey *ed25519.PublicKey Env *MockedEnv store kvstore.KVStore NodeConn *testchain.MockedNodeConn @@ -79,7 +79,7 @@ func NewMockedEnv(nodeCount int, t *testing.T, debug bool) (*MockedEnv, *ledgers Ledger: utxodb.New(), OriginatorKeyPair: nil, OriginatorAddress: nil, - Nodes: make(map[string]*MockedNode), + Nodes: make(map[ed25519.PublicKey]*MockedNode), } ret.OriginatorKeyPair, ret.OriginatorAddress = ret.Ledger.NewKeyPairByIndex(0) _, err := ret.Ledger.RequestFunds(ret.OriginatorAddress) @@ -107,9 +107,9 @@ func NewMockedEnv(nodeCount int, t *testing.T, debug bool) (*MockedEnv, *ledgers ret.NetworkBehaviour = testutil.NewPeeringNetDynamic(log) - nodeIDs, identities := testpeers.SetupKeys(uint16(nodeCount)) - ret.NodeIDs = nodeIDs - ret.NetworkProviders, ret.NetworkCloser = testpeers.SetupNet(ret.NodeIDs, identities, ret.NetworkBehaviour, log) + nodeIDs, nodeIdentities := testpeers.SetupKeys(uint16(nodeCount)) + ret.NodePubKeys = testpeers.PublicKeys(nodeIdentities) + ret.NetworkProviders, ret.NetworkCloser = testpeers.SetupNet(nodeIDs, nodeIdentities, ret.NetworkBehaviour, log) return ret, originTx } @@ -185,14 +185,15 @@ func (env *MockedEnv) PullConfirmedOutputFromLedger(addr ledgerstate.Address, ou } func (env *MockedEnv) NewMockedNode(nodeIndex int, timers StateManagerTimers) *MockedNode { - nodeID := env.NodeIDs[nodeIndex] - log := env.Log.Named(nodeID) - peers, err := env.NetworkProviders[nodeIndex].PeerDomain(env.ChainID.Array(), env.NodeIDs) + nodePubKey := env.NodePubKeys[nodeIndex] + nodePubKeyStr := nodePubKey.String()[0:10] + log := env.Log.Named(nodePubKeyStr) + peers, err := env.NetworkProviders[nodeIndex].PeerDomain(env.ChainID.Array(), env.NodePubKeys) require.NoError(env.T, err) ret := &MockedNode{ - NetID: nodeID, + PubKey: nodePubKey, Env: env, - NodeConn: testchain.NewMockedNodeConnection("Node_" + nodeID), + NodeConn: testchain.NewMockedNodeConnection("Node_" + nodePubKeyStr), store: mapdb.NewMapDB(), stateSync: coreutil.NewChainStateSync(), ChainCore: testchain.NewMockedChainCore(env.T, env.ChainID, log), @@ -209,7 +210,7 @@ func (env *MockedEnv) NewMockedNode(nodeIndex int, timers StateManagerTimers) *M return state.NewOptimisticStateReader(ret.store, ret.stateSync) }) ret.ChainPeers.Attach(peering.PeerMessageReceiverStateManager, func(peerMsg *peering.PeerMessageIn) { - log.Debugf("State manager recvEvent from %v of type %v", peerMsg.SenderNetID, peerMsg.MsgType) + log.Debugf("State manager recvEvent from %v of type %v", peerMsg.SenderPubKey.String(), peerMsg.MsgType) switch peerMsg.MsgType { case peerMsgTypeGetBlock: msg, err := messages.NewGetBlockMsg(peerMsg.MsgData) @@ -218,8 +219,8 @@ func (env *MockedEnv) NewMockedNode(nodeIndex int, timers StateManagerTimers) *M return } ret.StateManager.EnqueueGetBlockMsg(&messages.GetBlockMsgIn{ - GetBlockMsg: *msg, - SenderNetID: peerMsg.SenderNetID, + GetBlockMsg: *msg, + SenderPubKey: peerMsg.SenderPubKey, }) case peerMsgTypeBlock: msg, err := messages.NewBlockMsg(peerMsg.MsgData) @@ -228,8 +229,8 @@ func (env *MockedEnv) NewMockedNode(nodeIndex int, timers StateManagerTimers) *M return } ret.StateManager.EnqueueBlockMsg(&messages.BlockMsgIn{ - BlockMsg: *msg, - SenderNetID: peerMsg.SenderNetID, + BlockMsg: *msg, + SenderPubKey: peerMsg.SenderPubKey, }) } }) @@ -310,14 +311,14 @@ func (env *MockedEnv) AddNode(node *MockedNode) { env.mutex.Lock() defer env.mutex.Unlock() - if _, ok := env.Nodes[node.NetID]; ok { - env.Log.Panicf("AddNode: duplicate node index %s", node.NetID) + if _, ok := env.Nodes[*node.PubKey]; ok { + env.Log.Panicf("AddNode: duplicate node index %s", node.PubKey.String()) } - env.Nodes[node.NetID] = node + env.Nodes[*node.PubKey] = node } func (env *MockedEnv) RemoveNode(node *MockedNode) { env.mutex.Lock() defer env.mutex.Unlock() - delete(env.Nodes, node.NetID) + delete(env.Nodes, *node.PubKey) } diff --git a/packages/chain/statemgr/statemgr.go b/packages/chain/statemgr/statemgr.go index 2cbc6f1bfe..170419215c 100644 --- a/packages/chain/statemgr/statemgr.go +++ b/packages/chain/statemgr/statemgr.go @@ -99,8 +99,8 @@ func (sm *stateManager) receiveChainPeerMessages(peerMsg *peering.PeerMessageIn) return } sm.EnqueueGetBlockMsg(&messages.GetBlockMsgIn{ - GetBlockMsg: *msg, - SenderNetID: peerMsg.SenderNetID, + GetBlockMsg: *msg, + SenderPubKey: peerMsg.SenderPubKey, }) case peerMsgTypeBlock: msg, err := messages.NewBlockMsg(peerMsg.MsgData) @@ -109,8 +109,8 @@ func (sm *stateManager) receiveChainPeerMessages(peerMsg *peering.PeerMessageIn) return } sm.EnqueueBlockMsg(&messages.BlockMsgIn{ - BlockMsg: *msg, - SenderNetID: peerMsg.SenderNetID, + BlockMsg: *msg, + SenderPubKey: peerMsg.SenderPubKey, }) default: sm.log.Warnf("Wrong type of state manager message: %v, ignoring it", peerMsg.MsgType) diff --git a/packages/chain/statemgr/statemgr_test.go b/packages/chain/statemgr/statemgr_test.go index fc19db3900..0a296bc056 100644 --- a/packages/chain/statemgr/statemgr_test.go +++ b/packages/chain/statemgr/statemgr_test.go @@ -11,6 +11,7 @@ import ( "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/goshimmer/packages/ledgerstate/utxoutil" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/packages/chain" "github.com/iotaledger/wasp/packages/state" "github.com/stretchr/testify/require" @@ -267,7 +268,7 @@ func TestNodeDisconnected(t *testing.T) { // Single node gets disconnected until state 6 handlerName := "DisconnectedPeer" - env.NetworkBehaviour.WithPeerDisconnected(&handlerName, disconnectedNode.NetID) + env.NetworkBehaviour.WithPeerDisconnected(&handlerName, disconnectedNode.PubKey) const targetBlockIndex2 = 6 connectedNodes[0].OnStateTransitionMakeNewStateTransition(targetBlockIndex2) connectedNodes[0].MakeNewStateTransition() @@ -286,7 +287,7 @@ func TestNodeDisconnected(t *testing.T) { waitSyncBlockIndexAndCheck(10*time.Second, t, disconnectedNode, targetBlockIndex3) // Node, producing transitions, gets disconnected until state 12 - env.NetworkBehaviour.WithPeerDisconnected(&handlerName, disconnectedNode.NetID) + env.NetworkBehaviour.WithPeerDisconnected(&handlerName, disconnectedNode.PubKey) const targetBlockIndex4 = 12 connectedNodes[0].OnStateTransitionDoNothing() disconnectedNode.OnStateTransitionMakeNewStateTransition(targetBlockIndex4) @@ -337,16 +338,17 @@ func TestCruelWorld(t *testing.T) { env.AddNode(nodes[i]) } - var disconnectedNodes []string + var disconnectedNodes []*ed25519.PublicKey var mutex sync.Mutex go func() { // Connection cutter for { time.Sleep(randFromIntervalFun(1000, 3000) * time.Millisecond) mutex.Lock() - nodeName := nodes[rand.Intn(numberOfPeers)].NetID - env.NetworkBehaviour.WithPeerDisconnected(&nodeName, nodeName) - env.Log.Debugf("Connection to node %v lost", nodeName) - disconnectedNodes = append(disconnectedNodes, nodeName) + nodePubkey := nodes[rand.Intn(numberOfPeers)].PubKey + handlerID := nodePubkey.String() + env.NetworkBehaviour.WithPeerDisconnected(&handlerID, nodePubkey) + env.Log.Debugf("Connection to node %v lost", nodePubkey.String()) + disconnectedNodes = append(disconnectedNodes, nodePubkey) mutex.Unlock() } }() @@ -356,9 +358,9 @@ func TestCruelWorld(t *testing.T) { time.Sleep(randFromIntervalFun(500, 2000) * time.Millisecond) mutex.Lock() if len(disconnectedNodes) > 0 { - env.NetworkBehaviour.RemoveHandler(disconnectedNodes[0]) + env.NetworkBehaviour.RemoveHandler(disconnectedNodes[0].String()) env.Log.Debugf("Connection to node %v restored", disconnectedNodes[0]) - disconnectedNodes[0] = "" + disconnectedNodes[0] = nil disconnectedNodes = disconnectedNodes[1:] } } diff --git a/packages/chains/chains.go b/packages/chains/chains.go index 453f1c0f1d..688356398a 100644 --- a/packages/chains/chains.go +++ b/packages/chains/chains.go @@ -139,7 +139,6 @@ func (c *Chains) Activate(chr *registry.ChainRecord, registryProvider registry.P c.networkProvider, defaultRegistry, defaultRegistry, - defaultRegistry, c.processorConfig, c.offledgerBroadcastUpToNPeers, c.offledgerBroadcastInterval, diff --git a/packages/dkg/messages.go b/packages/dkg/messages.go index a5372b1c46..9b1a9ea33b 100644 --- a/packages/dkg/messages.go +++ b/packages/dkg/messages.go @@ -183,9 +183,8 @@ type initiatorInitMsg struct { step byte dkgRef string // Some unique string to identify duplicate initialization. peeringID peering.PeeringID - peerNetIDs []string - peerPubs []ed25519.PublicKey - initiatorPub ed25519.PublicKey + peerPubs []*ed25519.PublicKey + initiatorPub *ed25519.PublicKey threshold uint16 timeout time.Duration roundRetry time.Duration @@ -193,7 +192,7 @@ type initiatorInitMsg struct { type initiatorInitMsgIn struct { initiatorInitMsg - SenderNetID string + SenderPubKey *ed25519.PublicKey } func (m *initiatorInitMsg) MsgType() byte { @@ -220,9 +219,6 @@ func (m *initiatorInitMsg) Write(w io.Writer) error { if _, err = w.Write(m.peeringID[:]); err != nil { return err } - if err = util.WriteStrings16(w, m.peerNetIDs); err != nil { - return err - } if err = util.WriteUint16(w, uint16(len(m.peerPubs))); err != nil { return err } @@ -260,30 +256,31 @@ func (m *initiatorInitMsg) Read(r io.Reader) error { return fmt.Errorf("error while reading peering ID: read %v bytes, expected %v bytes", n, ledgerstate.AddressLength) } - if m.peerNetIDs, err = util.ReadStrings16(r); err != nil { - return err - } var arrLen uint16 if err = util.ReadUint16(r, &arrLen); err != nil { return err } - m.peerPubs = make([]ed25519.PublicKey, arrLen) + m.peerPubs = make([]*ed25519.PublicKey, arrLen) for i := range m.peerPubs { var peerPubBytes []byte if peerPubBytes, err = util.ReadBytes16(r); err != nil { return err } - if m.peerPubs[i], _, err = ed25519.PublicKeyFromBytes(peerPubBytes); err != nil { + peerPubKey, _, err := ed25519.PublicKeyFromBytes(peerPubBytes) + if err != nil { return err } + m.peerPubs[i] = &peerPubKey } var initiatorPubBytes []byte if initiatorPubBytes, err = util.ReadBytes16(r); err != nil { return err } - if m.initiatorPub, _, err = ed25519.PublicKeyFromBytes(initiatorPubBytes); err != nil { + initiatorPub, _, err := ed25519.PublicKeyFromBytes(initiatorPubBytes) + if err != nil { return err } + m.initiatorPub = &initiatorPub if err = util.ReadUint16(r, &m.threshold); err != nil { return err } diff --git a/packages/dkg/node.go b/packages/dkg/node.go index 75fd0dfbed..39fb8ae680 100644 --- a/packages/dkg/node.go +++ b/packages/dkg/node.go @@ -86,7 +86,7 @@ func (n *Node) receiveInitMessage(peerMsg *peering.PeerMessageIn) { } n.initMsgQueue <- &initiatorInitMsgIn{ initiatorInitMsg: *msg, - SenderNetID: peerMsg.SenderNetID, + SenderPubKey: peerMsg.SenderPubKey, } } @@ -99,16 +99,15 @@ func (n *Node) Close() { // This function is executed on the DKG initiator node (a chosen leader for this DKG instance). //nolint:funlen,gocritic func (n *Node) GenerateDistributedKey( - peerNetIDs []string, - peerPubs []ed25519.PublicKey, + peerPubs []*ed25519.PublicKey, threshold uint16, roundRetry time.Duration, // Retry for Peer <-> Peer communication. stepRetry time.Duration, // Retry for Initiator -> Peer communication. timeout time.Duration, // Timeout for the entire procedure. ) (*tcrypto.DKShare, error) { - n.log.Infof("Starting new DKG procedure, initiator=%v, peers=%+v", n.netProvider.Self().NetID(), peerNetIDs) + n.log.Infof("Starting new DKG procedure, initiator=%v, peers=%+v", n.netProvider.Self().NetID(), peerPubs) var err error - peerCount := uint16(len(peerNetIDs)) + peerCount := uint16(len(peerPubs)) // // Some validationfor the parameters. if peerCount < 1 || threshold < 1 || threshold > peerCount { @@ -123,7 +122,7 @@ func (n *Node) GenerateDistributedKey( // Setup network connections. dkgID := peering.RandomPeeringID() var netGroup peering.GroupProvider - if netGroup, err = n.netProvider.PeerGroup(dkgID, peerNetIDs); err != nil { + if netGroup, err = n.netProvider.PeerGroup(dkgID, peerPubs); err != nil { return nil, err } defer netGroup.Close() @@ -136,7 +135,7 @@ func (n *Node) GenerateDistributedKey( gTimeout := timeout if peerPubs == nil { // Take the public keys from the peering network, if they were not specified. - peerPubs = make([]ed25519.PublicKey, peerCount) + peerPubs = make([]*ed25519.PublicKey, peerCount) for i, n := range netGroup.AllNodes() { if err = n.Await(timeout); err != nil { return nil, err @@ -145,7 +144,7 @@ func (n *Node) GenerateDistributedKey( if nPub == nil { return nil, fmt.Errorf("Have no public key for %v", n.NetID()) } - peerPubs[i] = *nPub + peerPubs[i] = nPub } } // @@ -156,9 +155,8 @@ func (n *Node) GenerateDistributedKey( peer.SendMsg(makePeerMessage(initPeeringID, peering.PeerMessageReceiverDkgInit, rabinStep0Initialize, &initiatorInitMsg{ dkgRef: dkgID.String(), // It could be some other identifier. peeringID: dkgID, - peerNetIDs: peerNetIDs, peerPubs: peerPubs, - initiatorPub: n.identity.PublicKey, + initiatorPub: &n.identity.PublicKey, threshold: threshold, timeout: timeout, roundRetry: roundRetry, @@ -278,7 +276,7 @@ func (n *Node) onInitMsg(msg *initiatorInitMsgIn) { // To have idempotence for retries, we need to consider duplicate // messages as success, if process is already created. n.procLock.RUnlock() - n.netProvider.SendMsgByNetID(msg.SenderNetID, makePeerMessage(msg.peeringID, peering.PeerMessageReceiverDkg, msg.step, &initiatorStatusMsg{ + n.netProvider.SendMsgByPubKey(msg.SenderPubKey, makePeerMessage(msg.peeringID, peering.PeerMessageReceiverDkg, msg.step, &initiatorStatusMsg{ error: nil, })) return @@ -292,7 +290,7 @@ func (n *Node) onInitMsg(msg *initiatorInitMsgIn) { n.processes[p.dkgRef] = p } n.procLock.Unlock() - n.netProvider.SendMsgByNetID(msg.SenderNetID, makePeerMessage(msg.peeringID, peering.PeerMessageReceiverDkg, msg.step, &initiatorStatusMsg{ + n.netProvider.SendMsgByPubKey(msg.SenderPubKey, makePeerMessage(msg.peeringID, peering.PeerMessageReceiverDkg, msg.step, &initiatorStatusMsg{ error: err, })) }() @@ -335,7 +333,7 @@ func (n *Node) exchangeInitiatorAcks( sendCB func(peerIdx uint16, peer peering.PeerSender), ) error { recvCB := func(recv *peering.PeerMessageGroupIn, msg initiatorMsg) (bool, error) { - n.log.Debugf("Initiator recv. step=%v response %v from %v", step, msg, recv.SenderNetID) + n.log.Debugf("Initiator recv. step=%v response %v from %v", step, msg, recv.SenderPubKey.String()) return true, nil } return n.exchangeInitiatorMsgs(netGroup, peers, recvCh, retryTimeout, giveUpTimeout, step, sendCB, recvCB) @@ -360,7 +358,7 @@ func (n *Node) exchangeInitiatorMsgs( return false, nil } if err != nil { - n.log.Warnf("Failed to read message from %v: %v", recv.SenderNetID, recv.PeerMessageData) + n.log.Warnf("Failed to read message from %v: %v", recv.SenderPubKey.String(), recv.PeerMessageData) return false, err } if !initMsg.IsResponse() { diff --git a/packages/dkg/node_test.go b/packages/dkg/node_test.go index 766bb6f409..093d2e9983 100644 --- a/packages/dkg/node_test.go +++ b/packages/dkg/node_test.go @@ -52,7 +52,6 @@ func TestBasic(t *testing.T) { // // Initiate the key generation from some client node. dkShare, err := dkgNodes[0].GenerateDistributedKey( - peerNetIDs, testpeers.PublicKeys(peerIdentities), threshold, 1*time.Second, @@ -64,50 +63,6 @@ func TestBasic(t *testing.T) { require.NotNil(t, dkShare.SharedPublic) } -// TestNoPubs checks, if public keys are taken from the peering network successfully. -// See a NOTE in the test case bellow. -func TestNoPubs(t *testing.T) { - log := testlogger.NewLogger(t) - defer log.Sync() - // - // Create a fake network and keys for the tests. - timeout := 100 * time.Second - var threshold uint16 = 10 - var peerCount uint16 = 10 - peerNetIDs, peerIdentities := testpeers.SetupKeys(peerCount) - var peeringNetwork *testutil.PeeringNetwork = testutil.NewPeeringNetwork( - peerNetIDs, peerIdentities, 10000, - testutil.NewPeeringNetReliable(log), - testlogger.WithLevel(log, logger.LevelWarn, false), - ) - var networkProviders []peering.NetworkProvider = peeringNetwork.NetworkProviders() - // - // Initialize the DKG subsystem in each node. - var dkgNodes []*dkg.Node = make([]*dkg.Node, len(peerNetIDs)) - for i := range peerNetIDs { - registry := testutil.NewDkgRegistryProvider(tcrypto.DefaultSuite()) - dkgNode, err := dkg.NewNode( - peerIdentities[i], networkProviders[i], registry, - testlogger.WithLevel(log.With("NetID", peerNetIDs[i]), logger.LevelDebug, false), - ) - require.NoError(t, err) - dkgNodes[i] = dkgNode - } - // - // Initiate the key generation from some client node. - dkShare, err := dkgNodes[0].GenerateDistributedKey( - peerNetIDs, - nil, // NOTE: Should be taken from the peering node. - threshold, - 1*time.Second, - 2*time.Second, - timeout, - ) - require.Nil(t, err) - require.NotNil(t, dkShare.Address) - require.NotNil(t, dkShare.SharedPublic) -} - // TestUnreliableNet checks, if DKG runs on an unreliable network. // See a NOTE in the test case bellow. func TestUnreliableNet(t *testing.T) { @@ -148,8 +103,7 @@ func TestUnreliableNet(t *testing.T) { // // Initiate the key generation from some client node. dkShare, err := dkgNodes[0].GenerateDistributedKey( - peerNetIDs, - nil, // NOTE: Should be taken from the peering node. + testpeers.PublicKeys(peerIdentities), threshold, 100*time.Millisecond, // Round retry. 500*time.Millisecond, // Step retry. @@ -192,7 +146,6 @@ func TestLowN(t *testing.T) { // // Initiate the key generation from some client node. dkShare, err := dkgNodes[0].GenerateDistributedKey( - peerNetIDs, testpeers.PublicKeys(peerIdentities), threshold, 1*time.Second, diff --git a/packages/dkg/proc.go b/packages/dkg/proc.go index c037ddc064..95bcfd344f 100644 --- a/packages/dkg/proc.go +++ b/packages/dkg/proc.go @@ -44,7 +44,7 @@ type proc struct { dkShare *tcrypto.DKShare // This will be generated as a result of this procedure. node *Node // DKG node we are running in. nodeIndex uint16 // Index of this node. - initiatorPub ed25519.PublicKey + initiatorPub *ed25519.PublicKey threshold uint16 roundRetry time.Duration // Retry period for the Peer <-> Peer communication. netGroup peering.GroupProvider // A group for which the distributed key is generated. @@ -53,7 +53,7 @@ type proc struct { attachID interface{} // We keep it here to be able to detach from the network. peerMsgCh chan *peering.PeerMessageGroupIn // A buffer for the received peer messages. log *logger.Logger // A logger to use. - myNetID string // Just to make logging easier. + myPubKey *ed25519.PublicKey // Just to make logging easier. steps map[byte]*procStep // All the steps for the procedure. } @@ -62,7 +62,7 @@ func onInitiatorInit(dkgID peering.PeeringID, msg *initiatorInitMsg, node *Node) var err error var netGroup peering.GroupProvider - if netGroup, err = node.netProvider.PeerGroup(dkgID, msg.peerNetIDs); err != nil { + if netGroup, err = node.netProvider.PeerGroup(dkgID, msg.peerPubs); err != nil { return nil, err } var dkgImpl *rabin_dkg.DistKeyGenerator @@ -92,9 +92,9 @@ func onInitiatorInit(dkgID peering.PeeringID, msg *initiatorInitMsg, node *Node) dkgLock: &sync.RWMutex{}, peerMsgCh: make(chan *peering.PeerMessageGroupIn, len(msg.peerPubs)), log: log, - myNetID: node.netProvider.Self().NetID(), + myPubKey: node.netProvider.Self().PubKey(), } - p.log.Infof("Starting DKG Peer process at %v for DkgID=%v", p.myNetID, p.dkgID.String()) + p.log.Infof("Starting DKG Peer process at %v for DkgID=%v", p.myPubKey.String(), p.dkgID.String()) stepsStart := make(chan map[uint16]*peering.PeerMessageData) p.steps = make(map[byte]*procStep) if p.dkgImpl == nil { @@ -253,7 +253,7 @@ func (p *proc) rabinStep2R22SendResponsesMakeSent(step byte, initRecv *peering.P return nil, err } p.dkgLock.Unlock() - p.log.Debugf("RabinDKG[%v] DealResponse[%v|%v]=%v", p.myNetID, r.Index, r.Response.Index, base58.Encode(r.Response.SessionID)) + p.log.Debugf("RabinDKG[%v] DealResponse[%v|%v]=%v", p.myPubKey.String(), r.Index, r.Response.Index, base58.Encode(r.Response.SessionID)) ourResponses = append(ourResponses, r) } // @@ -297,7 +297,7 @@ func (p *proc) rabinStep3R23SendJustificationsMakeSent(step byte, initRecv *peer for _, r := range recvResponses[i].responses { p.dkgLock.Lock() var j *rabin_dkg.Justification - p.log.Debugf("RabinDKG[%v] ProcResponse[%v|%v]=%v", p.myNetID, r.Index, r.Response.Index, base58.Encode(r.Response.SessionID)) + p.log.Debugf("RabinDKG[%v] ProcResponse[%v|%v]=%v", p.myPubKey.String(), r.Index, r.Response.Index, base58.Encode(r.Response.SessionID)) if j, err = p.dkgImpl.ProcessResponse(r); err != nil { p.dkgLock.Unlock() p.log.Errorf("ProcessResponse(%v) -> %+v, resp.SessionID=%v", i, err, base58.Encode(r.Response.SessionID)) @@ -737,7 +737,7 @@ func (s *procStep) run() { // messages from others. Maybe our messages were lost, so we just resend the same messages. if s.initResp != nil { if isDkgInitProcRecvMsg(recv.MsgType) { - s.log.Debugf("[%v -%v-> %v] Resending initiator response.", s.proc.myNetID, s.initResp.MsgType, recv.SenderNetID) + s.log.Debugf("[%v -%v-> %v] Resending initiator response.", s.proc.myPubKey.String(), s.initResp.MsgType, recv.SenderPubKey.String()) s.proc.netGroup.SendMsgByIndex(recv.SenderIndex, s.initResp.MsgReceiver, s.initResp.MsgType, s.initResp.MsgData) continue } @@ -750,7 +750,7 @@ func (s *procStep) run() { s.sendEcho(recv) continue } - s.log.Warnf("[%v -%v-> %v] Dropping unknown message.", recv.SenderNetID, recv.MsgType, s.proc.myNetID) + s.log.Warnf("[%v -%v-> %v] Dropping unknown message.", recv.SenderPubKey.String(), recv.MsgType, s.proc.node.pubKey.String()) continue } // @@ -765,8 +765,8 @@ func (s *procStep) run() { s.markDone(makePeerMessage(s.proc.dkgID, peering.PeerMessageReceiverDkg, s.step, &initiatorStatusMsg{error: err})) } for i := range s.sentMsgs { - netID, _ := s.proc.netGroup.NetIDByIndex(i) - s.log.Debugf("[%v -%v-> %v] Sending peer message (first).", s.proc.myNetID, s.sentMsgs[i].MsgType, netID) + pubKey, _ := s.proc.netGroup.PubKeyByIndex(i) + s.log.Debugf("[%v -%v-> %v] Sending peer message (first).", s.proc.myPubKey.String(), s.sentMsgs[i].MsgType, pubKey.String()) s.proc.netGroup.SendMsgByIndex(i, s.sentMsgs[i].MsgReceiver, s.sentMsgs[i].MsgType, s.sentMsgs[i].MsgData) } if s.haveAll() { @@ -790,15 +790,15 @@ func (s *procStep) run() { } continue } - s.log.Warnf("[%v -%v-> %v] Dropping unknown message.", recv.SenderNetID, recv.MsgType, s.proc.myNetID) + s.log.Warnf("[%v -%v-> %v] Dropping unknown message.", recv.SenderPubKey.String(), recv.MsgType, s.proc.myPubKey.String()) continue case <-s.retryCh: // Resend all the messages, from who we haven't received. s.retryCh = time.After(s.proc.roundRetry) // Repeat the timer. for i := range s.sentMsgs { if s.recvMsgs[i] == nil { - netID, _ := s.proc.netGroup.NetIDByIndex(i) - s.log.Debugf("[%v -%v-> %v] Resending peer message (retry).", s.proc.myNetID, s.sentMsgs[i].MsgType, netID) + pubKey, _ := s.proc.netGroup.PubKeyByIndex(i) + s.log.Debugf("[%v -%v-> %v] Resending peer message (retry).", s.proc.myPubKey.String(), s.sentMsgs[i].MsgType, pubKey.String()) s.proc.netGroup.SendMsgByIndex(i, s.sentMsgs[i].MsgReceiver, s.sentMsgs[i].MsgType, s.sentMsgs[i].MsgData) } } @@ -814,14 +814,14 @@ func (s *procStep) sendEcho(recv *peering.PeerMessageGroupIn) { if sentMsg, sentMsgOK := s.sentMsgs[recv.SenderIndex]; sentMsgOK { echoMsg := *sentMsg // Make a copy. if echoMsg.MsgType, err = makeDkgRoundEchoMsg(echoMsg.MsgType); err != nil { - s.log.Warnf("[%v -%v-> %v] Unable to send echo message, reason=%v", s.proc.myNetID, recv.MsgType, recv.SenderNetID, err) + s.log.Warnf("[%v -%v-> %v] Unable to send echo message, reason=%v", s.proc.myPubKey.String(), recv.MsgType, recv.SenderPubKey.String(), err) return } - s.log.Debugf("[%v -%v-> %v] Resending peer message (echo).", s.proc.myNetID, echoMsg.MsgType, recv.SenderNetID) + s.log.Debugf("[%v -%v-> %v] Resending peer message (echo).", s.proc.myPubKey.String(), echoMsg.MsgType, recv.SenderPubKey.String()) s.proc.netGroup.SendMsgByIndex(recv.SenderIndex, echoMsg.MsgReceiver, echoMsg.MsgType, echoMsg.MsgData) return } - s.log.Warnf("[%v -%v-> %v] Unable to send echo message, is was not produced yet.", s.proc.myNetID, recv.MsgType, recv.SenderNetID) + s.log.Warnf("[%v -%v-> %v] Unable to send echo message, is was not produced yet.", s.proc.myPubKey.String(), recv.MsgType, recv.SenderPubKey.String()) } func (s *procStep) haveAll() bool { @@ -852,8 +852,8 @@ func (s *procStep) markDone(initResp *peering.PeerMessageData) { if s.initRecv != nil { s.proc.netGroup.SendMsgByIndex(s.initRecv.SenderIndex, initResp.MsgReceiver, initResp.MsgType, initResp.MsgData) // Send response to the initiator. } else { - s.log.Panicf("Step %v/%v closed with no initiator message.", s.proc.myNetID, s.step) + s.log.Panicf("Step %v/%v closed with no initiator message.", s.proc.myPubKey.String(), s.step) } s.retryCh = nil // Cancel the retry timer. - s.log.Debugf("Step %v/%v marked as completed.", s.proc.myNetID, s.step) + s.log.Debugf("Step %v/%v marked as completed.", s.proc.myPubKey.String(), s.step) } diff --git a/packages/peering/domain/domain.go b/packages/peering/domain/domain.go index 6850891ff1..ccb4b28806 100644 --- a/packages/peering/domain/domain.go +++ b/packages/peering/domain/domain.go @@ -5,7 +5,6 @@ package domain import ( "crypto/rand" - "sort" "sync" "github.com/iotaledger/hive.go/crypto/ed25519" @@ -16,9 +15,9 @@ import ( type DomainImpl struct { netProvider peering.NetworkProvider - nodes map[string]peering.PeerSender + nodes map[ed25519.PublicKey]peering.PeerSender permutation *util.Permutation16 - netIDs []string + permPubKeys []*ed25519.PublicKey peeringID peering.PeeringID attachIDs []interface{} log *logger.Logger @@ -31,42 +30,28 @@ var _ peering.PeerDomainProvider = &DomainImpl{} func NewPeerDomain(netProvider peering.NetworkProvider, peeringID peering.PeeringID, initialNodes []peering.PeerSender, log *logger.Logger) *DomainImpl { ret := &DomainImpl{ netProvider: netProvider, - nodes: make(map[string]peering.PeerSender), + nodes: make(map[ed25519.PublicKey]peering.PeerSender), permutation: util.NewPermutation16(uint16(len(initialNodes)), nil), - netIDs: make([]string, 0, len(initialNodes)), + permPubKeys: make([]*ed25519.PublicKey, len(initialNodes)), peeringID: peeringID, attachIDs: make([]interface{}, 0), log: log, mutex: &sync.RWMutex{}, } - for _, sender := range initialNodes { - ret.nodes[sender.NetID()] = sender + for i, sender := range initialNodes { + ret.nodes[*sender.PubKey()] = sender + ret.permPubKeys[i] = sender.PubKey() } ret.reshufflePeers() return ret } -func NewPeerDomainByNetIDs(netProvider peering.NetworkProvider, peeringID peering.PeeringID, peerNetIDs []string, log *logger.Logger) (*DomainImpl, error) { - peers := make([]peering.PeerSender, 0, len(peerNetIDs)) - for _, nid := range peerNetIDs { - if nid == netProvider.Self().NetID() { - continue - } - peer, err := netProvider.PeerByNetID(nid) - if err != nil { - return nil, err - } - peers = append(peers, peer) - } - return NewPeerDomain(netProvider, peeringID, peers, log), nil -} - -func (d *DomainImpl) SendMsgByNetID(netID string, msgReceiver, msgType byte, msgData []byte) { +func (d *DomainImpl) SendMsgByPubKey(pubKey *ed25519.PublicKey, msgReceiver, msgType byte, msgData []byte) { d.mutex.RLock() defer d.mutex.RUnlock() - peer, ok := d.nodes[netID] + peer, ok := d.nodes[*pubKey] if !ok { - d.log.Warnf("SendMsgByNetID: NetID %v is not in the domain", netID) + d.log.Warnf("SendMsgByPubKey: PubKey %v is not in the domain", pubKey.String()) return } peer.SendMsg(&peering.PeerMessageData{ @@ -78,27 +63,27 @@ func (d *DomainImpl) SendMsgByNetID(netID string, msgReceiver, msgType byte, msg } func (d *DomainImpl) SendPeerMsgToRandomPeers(upToNumPeers int, msgReceiver, msgType byte, msgData []byte) { - for _, netID := range d.GetRandomPeers(upToNumPeers) { - d.SendMsgByNetID(netID, msgReceiver, msgType, msgData) + for _, pubKey := range d.GetRandomPeers(upToNumPeers) { + d.SendMsgByPubKey(pubKey, msgReceiver, msgType, msgData) } } -func (d *DomainImpl) GetRandomPeers(upToNumPeers int) []string { +func (d *DomainImpl) GetRandomPeers(upToNumPeers int) []*ed25519.PublicKey { d.mutex.RLock() defer d.mutex.RUnlock() - if upToNumPeers > len(d.netIDs) { - upToNumPeers = len(d.netIDs) + if upToNumPeers > len(d.permPubKeys) { + upToNumPeers = len(d.permPubKeys) } - ret := make([]string, upToNumPeers) + ret := make([]*ed25519.PublicKey, upToNumPeers) for i := range ret { - ret[i] = d.netIDs[d.permutation.Next()] + ret[i] = d.permPubKeys[d.permutation.Next()] } return ret } func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { d.mutex.RLock() - nodes := make(map[string]peering.PeerSender) + nodes := make(map[ed25519.PublicKey]peering.PeerSender) for k, v := range d.nodes { nodes[k] = v } @@ -121,7 +106,7 @@ func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { d.log.Warnf("Peer with pubKey=%v not found, will be ignored for now, reason: %v", newPeerPubKey.String(), err) } else { changed = true - nodes[newPeerSender.NetID()] = newPeerSender + nodes[*newPeerSender.PubKey()] = newPeerSender d.log.Infof("Domain peer added, pubKey=%v, netID=%v", newPeerSender.PubKey().String(), newPeerSender.NetID()) } } @@ -138,7 +123,7 @@ func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { } if !found && (*oldPeer.PubKey() != *d.netProvider.Self().PubKey()) { changed = true - delete(nodes, oldPeer.NetID()) + delete(nodes, *oldPeer.PubKey()) d.log.Infof("Domain peer removed, pubKey=%v, netID=%v", oldPeer.PubKey().String(), oldPeer.NetID()) } } @@ -157,11 +142,11 @@ func (d *DomainImpl) ReshufflePeers(seedBytes ...[]byte) { } func (d *DomainImpl) reshufflePeers(seedBytes ...[]byte) { - d.netIDs = make([]string, 0, len(d.nodes)) - for netID := range d.nodes { - d.netIDs = append(d.netIDs, netID) + d.permPubKeys = make([]*ed25519.PublicKey, 0, len(d.nodes)) + for pubKey := range d.nodes { + peerPubKey := pubKey + d.permPubKeys = append(d.permPubKeys, &peerPubKey) } - sort.Strings(d.netIDs) var seedB []byte if len(seedBytes) == 0 { var b [8]byte @@ -175,15 +160,15 @@ func (d *DomainImpl) reshufflePeers(seedBytes ...[]byte) { func (d *DomainImpl) Attach(receiver byte, callback func(recv *peering.PeerMessageIn)) interface{} { attachID := d.netProvider.Attach(&d.peeringID, receiver, func(recv *peering.PeerMessageIn) { - if recv.SenderNetID == d.netProvider.Self().NetID() { + if *recv.SenderPubKey == *d.netProvider.Self().PubKey() { d.log.Warnf("dropping message for receiver=%v MsgType=%v from %v: message from self.", - recv.MsgReceiver, recv.MsgType, recv.SenderNetID) + recv.MsgReceiver, recv.MsgType, recv.SenderPubKey.String()) return } - _, ok := d.nodes[recv.SenderNetID] + _, ok := d.nodes[*recv.SenderPubKey] if !ok { d.log.Warnf("dropping message for receiver=%v MsgType=%v from %v: it does not belong to the peer domain.", - recv.MsgReceiver, recv.MsgType, recv.SenderNetID) + recv.MsgReceiver, recv.MsgType, recv.SenderPubKey.String()) return } callback(recv) @@ -192,6 +177,14 @@ func (d *DomainImpl) Attach(receiver byte, callback func(recv *peering.PeerMessa return attachID } +func (d *DomainImpl) PeerStatus() []peering.PeerStatusProvider { + res := make([]peering.PeerStatusProvider, 0) + for _, v := range d.nodes { + res = append(res, v.Status()) + } + return res +} + func (d *DomainImpl) Detach(attachID interface{}) { d.netProvider.Detach(attachID) } diff --git a/packages/peering/domain/domain_test.go b/packages/peering/domain/domain_test.go index c5f1611808..bc890a6e85 100644 --- a/packages/peering/domain/domain_test.go +++ b/packages/peering/domain/domain_test.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package domain_test import ( @@ -18,6 +21,7 @@ func TestDomainProvider(t *testing.T) { nodeCount := 3 netIDs, nodeIdentities := testpeers.SetupKeys(uint16(nodeCount)) nodes, netCloser := testpeers.SetupNet(netIDs, nodeIdentities, testutil.NewPeeringNetReliable(log), log) + nodePubKeys := testpeers.PublicKeys(nodeIdentities) for i := range nodes { go nodes[i].Run(make(<-chan struct{})) } @@ -44,12 +48,12 @@ func TestDomainProvider(t *testing.T) { // // Create a group on one of nodes. var d peering.PeerDomainProvider - d, err := nodes[1].PeerDomain(peeringID, netIDs) + d, err := nodes[1].PeerDomain(peeringID, nodePubKeys) require.Nil(t, err) require.NotNil(t, d) - d.SendMsgByNetID(netIDs[0], receiver, 125, []byte{}) - d.SendMsgByNetID(netIDs[2], receiver, 125, []byte{}) + d.SendMsgByPubKey(nodePubKeys[0], receiver, 125, []byte{}) + d.SendMsgByPubKey(nodePubKeys[2], receiver, 125, []byte{}) <-doneCh0 <-doneCh2 // @@ -65,17 +69,18 @@ func TestRandom(t *testing.T) { nodeCount := 5 netIDs, nodeIdentities := testpeers.SetupKeys(uint16(nodeCount)) nodes, netCloser := testpeers.SetupNet(netIDs, nodeIdentities, testutil.NewPeeringNetReliable(log), log) + nodePubKeys := testpeers.PublicKeys(nodeIdentities) for i := range nodes { go nodes[i].Run(make(<-chan struct{})) } peeringID := peering.RandomPeeringID() // Create a group on 2 of nodes. - d1, err := nodes[1].PeerDomain(peeringID, netIDs) + d1, err := nodes[1].PeerDomain(peeringID, nodePubKeys) require.NoError(t, err) require.NotNil(t, d1) - d2, err := nodes[2].PeerDomain(peeringID, netIDs) + d2, err := nodes[2].PeerDomain(peeringID, nodePubKeys) require.NoError(t, err) require.NotNil(t, d1) @@ -88,10 +93,10 @@ func TestRandom(t *testing.T) { ii := i nodes[i].Attach(&peeringID, receiver, func(recv *peering.PeerMessageIn) { t.Logf("%d received", ii) - if netIDs[1] == recv.SenderNetID { + if nodePubKeys[1] == recv.SenderPubKey { r1++ } - if netIDs[2] == recv.SenderNetID { + if nodePubKeys[2] == recv.SenderPubKey { r2++ } wg.Done() @@ -102,11 +107,11 @@ func TestRandom(t *testing.T) { for i := 0; i < 5; i++ { wg.Add(sendTo * 2) t.Log("----------------------------------") - for _, netID := range d1.GetRandomPeers(sendTo) { - d1.SendMsgByNetID(netID, receiver, 125, []byte{}) + for _, pubKey := range d1.GetRandomPeers(sendTo) { + d1.SendMsgByPubKey(pubKey, receiver, 125, []byte{}) } - for _, netID := range d2.GetRandomPeers(sendTo) { - d2.SendMsgByNetID(netID, receiver, 125, []byte{}) + for _, pubKey := range d2.GetRandomPeers(sendTo) { + d2.SendMsgByPubKey(pubKey, receiver, 125, []byte{}) } wg.Wait() } diff --git a/packages/peering/group/group.go b/packages/peering/group/group.go index b931e34ba2..6f85c90891 100644 --- a/packages/peering/group/group.go +++ b/packages/peering/group/group.go @@ -9,6 +9,7 @@ import ( "fmt" "time" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/peering" "golang.org/x/xerrors" @@ -66,24 +67,24 @@ func (g *groupImpl) SelfIndex() uint16 { // PeerIndex implements peering.GroupProvider. func (g *groupImpl) PeerIndex(peer peering.PeerSender) (uint16, error) { - return g.PeerIndexByNetID(peer.NetID()) + return g.PeerIndexByPubKey(peer.PubKey()) } // PeerIndexByNetID implements peering.GroupProvider. -func (g *groupImpl) PeerIndexByNetID(peerNetID string) (uint16, error) { +func (g *groupImpl) PeerIndexByPubKey(peerPubKey *ed25519.PublicKey) (uint16, error) { for i := range g.nodes { - if g.nodes[i].NetID() == peerNetID { + if *g.nodes[i].PubKey() == *peerPubKey { return uint16(i), nil } } - return NotInGroup, errors.New("peer_not_found_by_net_id") + return NotInGroup, errors.New("peer not found by pubKey") } -func (g *groupImpl) NetIDByIndex(index uint16) (string, error) { +func (g *groupImpl) PubKeyByIndex(index uint16) (*ed25519.PublicKey, error) { if index < uint16(len(g.nodes)) { - return g.nodes[index].NetID(), nil + return g.nodes[index].PubKey(), nil } - return "", errors.New("peer_index_out_of_scope") + return nil, errors.New("peer index out of scope") } // SendMsgByIndex implements peering.GroupProvider. @@ -135,11 +136,11 @@ func (g *groupImpl) ExchangeRound( if !ok { return errors.New("recv_channel_closed") } - senderIndex, err := g.PeerIndexByNetID(recvMsgNoIndex.SenderNetID) + senderIndex, err := g.PeerIndexByPubKey(recvMsgNoIndex.SenderPubKey) if err != nil { g.log.Warnf( "Dropping message %v -> %v, MsgType=%v because of %v", - recvMsgNoIndex.SenderNetID, g.netProvider.Self().NetID(), + recvMsgNoIndex.SenderPubKey.String(), g.netProvider.Self().PubKey().String(), recvMsgNoIndex.MsgType, err, ) continue @@ -151,7 +152,7 @@ func (g *groupImpl) ExchangeRound( if acks[recvMsg.SenderIndex] { // Only consider first successful message. g.log.Warnf( "Dropping duplicate message %v -> %v, receiver=%v, MsgType=%v", - recvMsg.SenderNetID, g.netProvider.Self().NetID(), + recvMsg.SenderPubKey.String(), g.netProvider.Self().PubKey().String(), recvMsg.MsgReceiver, recvMsg.MsgType, ) continue @@ -234,13 +235,13 @@ func (g *groupImpl) OtherNodes(except ...uint16) map[uint16]peering.PeerSender { // for the messages according to the message source. func (g *groupImpl) Attach(receiver byte, callback func(recv *peering.PeerMessageGroupIn)) interface{} { attachID := g.netProvider.Attach(&g.peeringID, receiver, func(recv *peering.PeerMessageIn) { - idx, err := g.PeerIndexByNetID(recv.SenderNetID) + idx, err := g.PeerIndexByPubKey(recv.SenderPubKey) if idx == NotInGroup { err = xerrors.Errorf("sender does not belong to the group") } if err != nil { g.log.Warnf("dropping message for receiver=%v MsgType=%v from %v: %v.", - recv.MsgReceiver, recv.MsgType, recv.SenderNetID, err) + recv.MsgReceiver, recv.MsgType, recv.SenderPubKey.String(), err) return } gRecv := &peering.PeerMessageGroupIn{ diff --git a/packages/peering/group/group_test.go b/packages/peering/group/group_test.go index 02659f5359..0c7fa61567 100644 --- a/packages/peering/group/group_test.go +++ b/packages/peering/group/group_test.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package group_test import ( @@ -40,7 +43,7 @@ func TestGroupProvider(t *testing.T) { // // Create a group on one of nodes. var g peering.GroupProvider - g, err := nodes[0].PeerGroup(peeringID, netIDs) + g, err := nodes[0].PeerGroup(peeringID, testpeers.PublicKeys(nodeIdentities)) require.Nil(t, err) // // Broadcast a message and wait until it will be received on all the nodes. diff --git a/packages/peering/lpp/lppNetImpl.go b/packages/peering/lpp/lppNetImpl.go index 0eb6bf15d4..78dbd60bab 100644 --- a/packages/peering/lpp/lppNetImpl.go +++ b/packages/peering/lpp/lppNetImpl.go @@ -21,7 +21,6 @@ package lpp import ( "context" "encoding/binary" - "errors" "fmt" "io" "net" @@ -319,11 +318,11 @@ func (n *netImpl) Self() peering.PeerSender { } // Group creates peering.GroupProvider. -func (n *netImpl) PeerGroup(peeringID peering.PeeringID, peerNetIDs []string) (peering.GroupProvider, error) { +func (n *netImpl) PeerGroup(peeringID peering.PeeringID, peerPubKeys []*ed25519.PublicKey) (peering.GroupProvider, error) { var err error - groupPeers := make([]peering.PeerSender, len(peerNetIDs)) - for i := range peerNetIDs { - if groupPeers[i], err = n.usePeer(peerNetIDs[i]); err != nil { + groupPeers := make([]peering.PeerSender, len(peerPubKeys)) + for i := range peerPubKeys { + if groupPeers[i], err = n.usePeer(peerPubKeys[i]); err != nil { return nil, err } } @@ -331,13 +330,13 @@ func (n *netImpl) PeerGroup(peeringID peering.PeeringID, peerNetIDs []string) (p } // Domain creates peering.PeerDomainProvider. -func (n *netImpl) PeerDomain(peeringID peering.PeeringID, peerNetIDs []string) (peering.PeerDomainProvider, error) { - peers := make([]peering.PeerSender, 0, len(peerNetIDs)) - for _, nid := range peerNetIDs { - if nid == n.Self().NetID() { +func (n *netImpl) PeerDomain(peeringID peering.PeeringID, peerPubKeys []*ed25519.PublicKey) (peering.PeerDomainProvider, error) { + peers := make([]peering.PeerSender, 0, len(peerPubKeys)) + for _, peerPubKey := range peerPubKeys { + if *peerPubKey == *n.Self().PubKey() { continue } - p, err := n.usePeer(nid) + p, err := n.usePeer(peerPubKey) if err != nil { return nil, err } @@ -346,13 +345,15 @@ func (n *netImpl) PeerDomain(peeringID peering.PeeringID, peerNetIDs []string) ( return domain.NewPeerDomain(n, peeringID, peers, n.log), nil } -func (n *netImpl) SendMsgByNetID(netID string, msg *peering.PeerMessageData) { - peer, err := n.PeerByNetID(netID) +// SendMsgByPubKey sends a message to the specified peer. +func (n *netImpl) SendMsgByPubKey(pubKey *ed25519.PublicKey, msg *peering.PeerMessageData) { + peer, err := n.PeerByPubKey(pubKey) if err != nil { - n.log.Warnf("SendMsgByNetID: NetID %v is not in the network", netID) + n.log.Warnf("SendMsgByPubKey: PubKey %v is not in the network", pubKey.String()) return } peer.SendMsg(msg) + peer.Close() } // Attach implements peering.NetworkProvider. @@ -372,25 +373,10 @@ func (n *netImpl) Detach(attachID interface{}) { n.recvEvents.Detach(closure) } -// PeerByNetID implements peering.NetworkProvider. -func (n *netImpl) PeerByNetID(peerNetID string) (peering.PeerSender, error) { - return n.usePeer(peerNetID) -} - // PeerByPubKey implements peering.NetworkProvider. // NOTE: For now, only known nodes can be looked up by PubKey. -func (n *netImpl) PeerByPubKey(peerPub *ed25519.PublicKey) (peering.PeerSender, error) { - n.peersLock.RLock() - for i := range n.peers { - pk := n.peers[i].PubKey() - if pk != nil && *pk == *peerPub { // Compared as binaries. - peerPubKey := n.peers[i].NetID() - n.peersLock.RUnlock() - return n.PeerByNetID(peerPubKey) - } - } - n.peersLock.RUnlock() - return nil, errors.New("known peer not found by pubKey") +func (n *netImpl) PeerByPubKey(peerPubKey *ed25519.PublicKey) (peering.PeerSender, error) { + return n.usePeer(peerPubKey) } // PeerStatus implements peering.NetworkProvider. @@ -417,13 +403,13 @@ func (n *netImpl) PubKey() *ed25519.PublicKey { // SendMsg implements peering.PeerSender for the Self() node. func (n *netImpl) SendMsg(msg *peering.PeerMessageData) { // Don't go via the network, if sending a message to self. - n.triggerRecvEvents(n.Self().NetID(), &peering.PeerMessageNet{PeerMessageData: *msg}) + n.triggerRecvEvents(n.Self().PubKey(), &peering.PeerMessageNet{PeerMessageData: *msg}) } -func (n *netImpl) triggerRecvEvents(from string, msg *peering.PeerMessageNet) { +func (n *netImpl) triggerRecvEvents(from *ed25519.PublicKey, msg *peering.PeerMessageNet) { n.recvEvents.Trigger(&peering.PeerMessageIn{ PeerMessageData: msg.PeerMessageData, - SenderNetID: from, + SenderPubKey: from, }) } @@ -432,11 +418,21 @@ func (n *netImpl) IsAlive() bool { return true // This node is alive. } +// NumUsers implements peering.PeerStatusProvider for the Self() node. +func (n *netImpl) NumUsers() int { + return 1 +} + // Await implements peering.PeerSender for the Self() node. func (n *netImpl) Await(timeout time.Duration) error { return nil // This node is alive immediately. } +// Status implements peering.PeerSender interface for the remote peers. +func (n *netImpl) Status() peering.PeerStatusProvider { + return n +} + // Close implements peering.PeerSender for the Self() node. func (n *netImpl) Close() { // We will con close the connection of the own node. @@ -476,19 +472,19 @@ func (n *netImpl) TrustedPeers() ([]*peering.TrustedPeer, error) { return n.trusted.TrustedPeers() } -func (n *netImpl) usePeer(remoteNetID string) (peering.PeerSender, error) { - if remoteNetID == n.myNetID { +func (n *netImpl) usePeer(remotePubKey *ed25519.PublicKey) (peering.PeerSender, error) { + if *remotePubKey == n.nodeKeyPair.PublicKey { return n, nil } n.peersLock.Lock() defer n.peersLock.Unlock() for _, p := range n.peers { - if p.remoteNetID == remoteNetID { + if *p.remotePubKey == *remotePubKey { p.usePeer() return p, nil } } - return nil, xerrors.Errorf("peer %v is not trusted", remoteNetID) + return nil, xerrors.Errorf("peer %v is not trusted", remotePubKey) } func (n *netImpl) maintenanceLoop(stopCh chan bool) { diff --git a/packages/peering/lpp/lppNetImpl_test.go b/packages/peering/lpp/lppNetImpl_test.go index 02e5294146..1dfe44b146 100644 --- a/packages/peering/lpp/lppNetImpl_test.go +++ b/packages/peering/lpp/lppNetImpl_test.go @@ -46,11 +46,11 @@ func TestLPPPeeringImpl(t *testing.T) { go nodes[i].Run(make(<-chan struct{})) } - n0p2, err := nodes[0].PeerByNetID(netIDs[2]) + n0p2, err := nodes[0].PeerByPubKey(&keys[2].PublicKey) require.NoError(t, err) - n1p1, err := nodes[1].PeerByNetID(netIDs[1]) + n1p1, err := nodes[1].PeerByPubKey(&keys[1].PublicKey) require.NoError(t, err) - n2p0, err := nodes[2].PeerByNetID(netIDs[0]) + n2p0, err := nodes[2].PeerByPubKey(&keys[0].PublicKey) require.NoError(t, err) chain1 := peering.RandomPeeringID() diff --git a/packages/peering/lpp/lppPeer.go b/packages/peering/lpp/lppPeer.go index b5dbf946c9..a1235f6b5f 100644 --- a/packages/peering/lpp/lppPeer.go +++ b/packages/peering/lpp/lppPeer.go @@ -147,7 +147,7 @@ func (p *peer) recvLoop() { for msg := range p.recvPipe.Out() { peerMsg, ok := msg.(*peering.PeerMessageNet) if ok { - p.net.triggerRecvEvents(p.NetID(), peerMsg) + p.net.triggerRecvEvents(p.PubKey(), peerMsg) } } } @@ -211,7 +211,7 @@ func (p *peer) IsInbound() bool { return p.remoteNetID < p.net.myNetID } -// IsInbound implements peering.PeerStatusProvider. +// NumUsers implements peering.PeerStatusProvider. // It is used in the dashboard. func (p *peer) NumUsers() int { p.accessLock.RLock() @@ -219,6 +219,11 @@ func (p *peer) NumUsers() int { return p.numUsers } +// Status implements peering.PeerSender interface for the remote peers. +func (p *peer) Status() peering.PeerStatusProvider { + return p +} + // SendMsg implements peering.PeerSender interface for the remote peers. func (p *peer) Close() { p.accessLock.Lock() diff --git a/packages/peering/peer_message.go b/packages/peering/peer_message.go index 11f9851e29..f80a0999f9 100644 --- a/packages/peering/peer_message.go +++ b/packages/peering/peer_message.go @@ -13,6 +13,7 @@ package peering import ( "bytes" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/packages/hashing" "github.com/iotaledger/wasp/packages/util" "github.com/iotaledger/wasp/packages/util/pipe" @@ -34,7 +35,7 @@ type PeerMessageNet struct { type PeerMessageIn struct { PeerMessageData - SenderNetID string + SenderPubKey *ed25519.PublicKey } type PeerMessageGroupIn struct { diff --git a/packages/peering/peering.go b/packages/peering/peering.go index 12c21a9257..999ca96903 100644 --- a/packages/peering/peering.go +++ b/packages/peering/peering.go @@ -38,14 +38,13 @@ const ( type NetworkProvider interface { Run(stopCh <-chan struct{}) Self() PeerSender - PeerGroup(peeringID PeeringID, peerAddrs []string) (GroupProvider, error) - PeerDomain(peeringID PeeringID, peerAddrs []string) (PeerDomainProvider, error) - PeerByNetID(peerNetID string) (PeerSender, error) + PeerGroup(peeringID PeeringID, peerPubKeys []*ed25519.PublicKey) (GroupProvider, error) + PeerDomain(peeringID PeeringID, peerAddrs []*ed25519.PublicKey) (PeerDomainProvider, error) PeerByPubKey(peerPub *ed25519.PublicKey) (PeerSender, error) + SendMsgByPubKey(pubKey *ed25519.PublicKey, msg *PeerMessageData) PeerStatus() []PeerStatusProvider Attach(peeringID *PeeringID, receiver byte, callback func(recv *PeerMessageIn)) interface{} Detach(attachID interface{}) - SendMsgByNetID(netID string, msg *PeerMessageData) } // TrustedNetworkManager is used maintain a configuration which peers are trusted. @@ -68,8 +67,8 @@ type TrustedNetworkManager interface { type GroupProvider interface { SelfIndex() uint16 PeerIndex(peer PeerSender) (uint16, error) - PeerIndexByNetID(peerNetID string) (uint16, error) - NetIDByIndex(index uint16) (string, error) + PeerIndexByPubKey(peerPubKey *ed25519.PublicKey) (uint16, error) + PubKeyByIndex(index uint16) (*ed25519.PublicKey, error) Attach(receiver byte, callback func(recv *PeerMessageGroupIn)) interface{} Detach(attachID interface{}) SendMsgByIndex(peerIdx uint16, msgReceiver byte, msgType byte, msgData []byte) @@ -91,12 +90,13 @@ type GroupProvider interface { // All peers in the domain shares same peeringID. Each peer within domain is identified via its netID type PeerDomainProvider interface { ReshufflePeers(seedBytes ...[]byte) - GetRandomPeers(upToNumPeers int) []string + GetRandomPeers(upToNumPeers int) []*ed25519.PublicKey UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) Attach(receiver byte, callback func(recv *PeerMessageIn)) interface{} Detach(attachID interface{}) - SendMsgByNetID(netID string, msgReceiver byte, msgType byte, msgData []byte) + SendMsgByPubKey(pubKey *ed25519.PublicKey, msgReceiver byte, msgType byte, msgData []byte) SendPeerMsgToRandomPeers(upToNumPeers int, msgReceiver byte, msgType byte, msgData []byte) + PeerStatus() []PeerStatusProvider Close() } @@ -124,6 +124,9 @@ type PeerSender interface { // public key resolved. Await(timeout time.Duration) error + // Provides a read-only representation of this sender. + Status() PeerStatusProvider + // Close releases the reference to the peer, this informs the network // implementation, that it can disconnect, cleanup resources, etc. // You need to get new reference to the peer (PeerSender) to use it again. diff --git a/packages/registry/committe_record_test.go b/packages/registry/committe_record_test.go deleted file mode 100644 index 2350ed6f67..0000000000 --- a/packages/registry/committe_record_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package registry - -import ( - "testing" - - "github.com/iotaledger/goshimmer/packages/ledgerstate" - "github.com/iotaledger/hive.go/crypto/ed25519" - "github.com/stretchr/testify/require" -) - -func TestCommitteeRecord(t *testing.T) { - keyPair := ed25519.GenerateKeyPair() - addr := ledgerstate.NewED25519Address(keyPair.PublicKey) - rec := NewCommitteeRecord(addr, "node:111", "node:333") - recBack, err := CommitteeRecordFromBytes(rec.Bytes()) - require.NoError(t, err) - require.True(t, rec.Address.Equals(recBack.Address)) - require.EqualValues(t, rec.Nodes, recBack.Nodes) - - t.Logf("%s", rec) -} diff --git a/packages/registry/committee_record.go b/packages/registry/committee_record.go deleted file mode 100644 index fd0109bcca..0000000000 --- a/packages/registry/committee_record.go +++ /dev/null @@ -1,72 +0,0 @@ -package registry - -import ( - "fmt" - - "github.com/iotaledger/goshimmer/packages/ledgerstate" - "github.com/iotaledger/hive.go/marshalutil" -) - -// CommitteeRecord represents committee information -// TODO optimize: no need to persists address in the structure -type CommitteeRecord struct { - Address ledgerstate.Address - Nodes []string // "host_addr:port" -} - -// NewCommitteeRecord -func NewCommitteeRecord(addr ledgerstate.Address, nodes ...string) *CommitteeRecord { - ret := &CommitteeRecord{ - Address: addr, - Nodes: make([]string, len(nodes)), - } - copy(ret.Nodes, nodes) - return ret -} - -// CommitteeRecordFromMarshalUtil -func CommitteeRecordFromMarshalUtil(mu *marshalutil.MarshalUtil) (*CommitteeRecord, error) { - ret := &CommitteeRecord{} - var err error - ret.Address, err = ledgerstate.AddressFromMarshalUtil(mu) - if err != nil { - return nil, err - } - numNodes, err := mu.ReadUint16() - if err != nil { - return nil, err - } - ret.Nodes = make([]string, numNodes) - for i := uint16(0); i < numNodes; i++ { - strSize, err := mu.ReadUint16() - if err != nil { - return nil, err - } - d, err := mu.ReadBytes(int(strSize)) - if err != nil { - return nil, err - } - ret.Nodes[i] = string(d) - } - return ret, nil -} - -// CommitteeRecordFromBytes -func CommitteeRecordFromBytes(data []byte) (*CommitteeRecord, error) { - return CommitteeRecordFromMarshalUtil(marshalutil.New(data)) -} - -func (rec *CommitteeRecord) Bytes() []byte { - mu := marshalutil.New(). - WriteBytes(rec.Address.Bytes()). - WriteUint16(uint16(len(rec.Nodes))) - for _, s := range rec.Nodes { - b := []byte(s) - mu.WriteUint16(uint16(len(b))).WriteBytes(b) - } - return mu.Bytes() -} - -func (rec *CommitteeRecord) String() string { - return fmt.Sprintf("Committee(Address: %s Nodes:%+v)", rec.Address.Base58(), rec.Nodes) -} diff --git a/packages/registry/providers.go b/packages/registry/providers.go index 8d05afdaa2..b484583b12 100644 --- a/packages/registry/providers.go +++ b/packages/registry/providers.go @@ -35,11 +35,6 @@ type DKShareRegistryProvider interface { LoadDKShare(sharedAddress ledgerstate.Address) (*tcrypto.DKShare, error) } -type CommitteeRegistryProvider interface { - GetCommitteeRecord(addr ledgerstate.Address) (*CommitteeRecord, error) - SaveCommitteeRecord(rec *CommitteeRecord) error -} - // ChainRecordRegistryProvider stands for a partial registry interface, needed for this package. type ChainRecordRegistryProvider interface { GetChainRecordByChainID(chainID *iscp.ChainID) (*ChainRecord, error) diff --git a/packages/registry/registry_impl.go b/packages/registry/registry_impl.go index df426972ef..525d823a2c 100644 --- a/packages/registry/registry_impl.go +++ b/packages/registry/registry_impl.go @@ -112,29 +112,6 @@ func (r *Impl) SaveChainRecord(rec *ChainRecord) error { // endregion /////////////////////////////////////////////////////////////// -// region CommitteeRegistryProvider /////////////////////////////////////////////////////////// - -func dbKeyCommitteeRecord(addr ledgerstate.Address) []byte { - return dbkeys.MakeKey(dbkeys.ObjectTypeCommitteeRecord, addr.Bytes()) -} - -func (r *Impl) GetCommitteeRecord(addr ledgerstate.Address) (*CommitteeRecord, error) { - data, err := r.store.Get(dbKeyCommitteeRecord(addr)) - if errors.Is(err, kvstore.ErrKeyNotFound) { - return nil, nil - } - if err != nil { - return nil, err - } - return CommitteeRecordFromBytes(data) -} - -func (r *Impl) SaveCommitteeRecord(rec *CommitteeRecord) error { - return r.store.Set(dbKeyCommitteeRecord(rec.Address), rec.Bytes()) -} - -// endregion ////////////////////////////////////////////////////////////////////// - // region DKShareRegistryProvider //////////////////////////////////////////////////// // SaveDKShare implements dkg.DKShareRegistryProvider. diff --git a/packages/testutil/peeringNetBehaviour.go b/packages/testutil/peeringNetBehaviour.go index 5e1f52fb8c..2b8118be93 100644 --- a/packages/testutil/peeringNetBehaviour.go +++ b/packages/testutil/peeringNetBehaviour.go @@ -11,12 +11,13 @@ import ( "math/rand" "time" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" ) // An interface for all the network behaviors. type PeeringNetBehavior interface { - AddLink(inCh, outCh chan *peeringMsg, dstNetID string) + AddLink(inCh, outCh chan *peeringMsg, dstPubKey *ed25519.PublicKey) Close() } @@ -36,7 +37,7 @@ func NewPeeringNetReliable(log *logger.Logger) PeeringNetBehavior { } // Run implements PeeringNetBehavior. -func (n *peeringNetReliable) AddLink(inCh, outCh chan *peeringMsg, dstNetID string) { +func (n *peeringNetReliable) AddLink(inCh, outCh chan *peeringMsg, dstPubKey *ed25519.PublicKey) { closeCh := make(chan bool) n.closeChs = append(n.closeChs, closeCh) go n.recvLoop(inCh, outCh, closeCh) @@ -83,10 +84,10 @@ func NewPeeringNetUnreliable(deliverPct, repeatPct int, delayFrom, delayTill tim } // Run implements PeeringNetBehavior. -func (n *peeringNetUnreliable) AddLink(inCh, outCh chan *peeringMsg, dstNetID string) { +func (n *peeringNetUnreliable) AddLink(inCh, outCh chan *peeringMsg, dstPubKey *ed25519.PublicKey) { closeCh := make(chan bool) n.closeChs = append(n.closeChs, closeCh) - go n.recvLoop(inCh, outCh, closeCh, dstNetID) + go n.recvLoop(inCh, outCh, closeCh, dstPubKey) } // Close implements PeeringNetBehavior. @@ -96,7 +97,7 @@ func (n *peeringNetUnreliable) Close() { } } -func (n *peeringNetUnreliable) recvLoop(inCh, outCh chan *peeringMsg, closeCh chan bool, dstNetID string) { +func (n *peeringNetUnreliable) recvLoop(inCh, outCh chan *peeringMsg, closeCh chan bool, dstPubKey *ed25519.PublicKey) { for { select { case <-closeCh: @@ -106,7 +107,7 @@ func (n *peeringNetUnreliable) recvLoop(inCh, outCh chan *peeringMsg, closeCh ch return } if rand.Intn(100) > n.deliverPct { - n.log.Debugf("Network dropped message %v -%v-> %v", recv.from, recv.msg.MsgType, dstNetID) + n.log.Debugf("Network dropped message %v -%v-> %v", recv.from.String(), recv.msg.MsgType, dstPubKey.String()) continue // Drop the message. } // @@ -117,13 +118,13 @@ func (n *peeringNetUnreliable) recvLoop(inCh, outCh chan *peeringMsg, closeCh ch numRepeat++ } for i := 0; i < numRepeat; i++ { - go n.sendDelayed(recv, outCh, dstNetID, i+1, numRepeat) + go n.sendDelayed(recv, outCh, dstPubKey, i+1, numRepeat) } } } } -func (n *peeringNetUnreliable) sendDelayed(recv *peeringMsg, outCh chan *peeringMsg, dstNetID string, dupNum, dupCount int) { +func (n *peeringNetUnreliable) sendDelayed(recv *peeringMsg, outCh chan *peeringMsg, dstPubKey *ed25519.PublicKey, dupNum, dupCount int) { fromMS := int(n.delayFrom.Milliseconds()) tillMS := int(n.delayTill.Milliseconds()) var delay time.Duration @@ -137,7 +138,7 @@ func (n *peeringNetUnreliable) sendDelayed(recv *peeringMsg, outCh chan *peering } n.log.Debugf( "Network delivers message %v -%v-> %v (duplicate %v/%v, delay=%vms)", - recv.from, recv.msg.MsgType, dstNetID, dupNum, dupCount, delay.Milliseconds(), + recv.from.String(), recv.msg.MsgType, dstPubKey.String(), dupNum, dupCount, delay.Milliseconds(), ) safeSendPeeringMsg(outCh, recv, n.log) } diff --git a/packages/testutil/peeringNetBehaviourDynamic.go b/packages/testutil/peeringNetBehaviourDynamic.go index f3765d54c4..70acf6cc85 100644 --- a/packages/testutil/peeringNetBehaviourDynamic.go +++ b/packages/testutil/peeringNetBehaviourDynamic.go @@ -11,6 +11,7 @@ import ( "sync" "time" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" ) @@ -34,7 +35,7 @@ type peeringNetDynamicHandlerEntry struct { type peeringNetDynamicHandler interface { handleSendMessage( msg *peeringMsg, - dstNetID string, + dstPubKey *ed25519.PublicKey, nextHandlers []peeringNetDynamicHandlerEntry, callHandlersAndSendFun func(nextHandlers []peeringNetDynamicHandlerEntry), log *logger.Logger, @@ -81,11 +82,11 @@ func (pndT *PeeringNetDynamic) WithDelayingChannel(id *string, delayFrom, delayT return pndT } -func (pndT *PeeringNetDynamic) WithPeerDisconnected(id *string, peerName string) *PeeringNetDynamic { +func (pndT *PeeringNetDynamic) WithPeerDisconnected(id *string, peerPubKey *ed25519.PublicKey) *PeeringNetDynamic { pndT.addHandlerEntry(peeringNetDynamicHandlerEntry{ id, &peeringNetDynamicHandlerPeerDisconnected{ - peerName: peerName, + peerPubKey: peerPubKey, }, }) return pndT @@ -114,10 +115,10 @@ func (pndT *PeeringNetDynamic) RemoveHandler(id string) bool { } // Run implements PeeringNetBehavior. -func (pndT *PeeringNetDynamic) AddLink(inCh, outCh chan *peeringMsg, dstNetID string) { +func (pndT *PeeringNetDynamic) AddLink(inCh, outCh chan *peeringMsg, dstPubKey *ed25519.PublicKey) { closeCh := make(chan bool) pndT.closeChs = append(pndT.closeChs, closeCh) - go pndT.recvLoop(inCh, outCh, closeCh, dstNetID) + go pndT.recvLoop(inCh, outCh, closeCh, dstPubKey) } // Close implements PeeringNetBehavior. @@ -127,7 +128,7 @@ func (pndT *PeeringNetDynamic) Close() { } } -func (pndT *PeeringNetDynamic) recvLoop(inCh, outCh chan *peeringMsg, closeCh chan bool, dstNetID string) { +func (pndT *PeeringNetDynamic) recvLoop(inCh, outCh chan *peeringMsg, closeCh chan bool, dstPubKey *ed25519.PublicKey) { for { select { case <-closeCh: @@ -139,9 +140,9 @@ func (pndT *PeeringNetDynamic) recvLoop(inCh, outCh chan *peeringMsg, closeCh ch var callHandlersAndSendFun func(nextHandlers []peeringNetDynamicHandlerEntry) callHandlersAndSendFun = func(nextHandlers []peeringNetDynamicHandlerEntry) { if len(nextHandlers) > 0 { - nextHandlers[0].handleSendMessage(recv, dstNetID, nextHandlers[1:], callHandlersAndSendFun, pndT.log) + nextHandlers[0].handleSendMessage(recv, dstPubKey, nextHandlers[1:], callHandlersAndSendFun, pndT.log) } else { - pndT.log.Debugf("Network delivers message %v -%v-> %v", recv.from, recv.msg.MsgType, dstNetID) + pndT.log.Debugf("Network delivers message %v -%v-> %v", recv.from.String(), recv.msg.MsgType, dstPubKey.String()) safeSendPeeringMsg(outCh, recv, pndT.log) } } @@ -164,13 +165,13 @@ type peeringNetDynamicHandlerLosingChannel struct { func (lcT *peeringNetDynamicHandlerLosingChannel) handleSendMessage( msg *peeringMsg, - dstNetID string, + dstPubKey *ed25519.PublicKey, nextHandlers []peeringNetDynamicHandlerEntry, callHandlersAndSendFun func(nextHandlers []peeringNetDynamicHandlerEntry), log *logger.Logger, ) { if rand.Intn(100) > lcT.probability { - log.Debugf("Network dropped message %v -%v-> %v", msg.from, msg.msg.MsgType, dstNetID) + log.Debugf("Network dropped message %v -%v-> %v", msg.from.String(), msg.msg.MsgType, dstPubKey.String()) return } callHandlersAndSendFun(nextHandlers) @@ -182,7 +183,7 @@ type peeringNetDynamicHandlerRepeatingChannel struct { func (rcT *peeringNetDynamicHandlerRepeatingChannel) handleSendMessage( msg *peeringMsg, - dstNetID string, + dstPubKey *ed25519.PublicKey, nextHandlers []peeringNetDynamicHandlerEntry, callHandlersAndSendFun func(nextHandlers []peeringNetDynamicHandlerEntry), log *logger.Logger, @@ -191,7 +192,7 @@ func (rcT *peeringNetDynamicHandlerRepeatingChannel) handleSendMessage( if rand.Intn(100) < rcT.probability%100 { numRepeat++ } - log.Debugf("Network repeated message %v -%v-> %v %v times", msg.from, msg.msg.MsgType, dstNetID, numRepeat) + log.Debugf("Network repeated message %v -%v-> %v %v times", msg.from.String(), msg.msg.MsgType, dstPubKey.String(), numRepeat) for i := 0; i < numRepeat; i++ { callHandlersAndSendFun(nextHandlers) } @@ -204,7 +205,7 @@ type peeringNetDynamicHandlerDelayingChannel struct { func (dcT *peeringNetDynamicHandlerDelayingChannel) handleSendMessage( msg *peeringMsg, - dstNetID string, + dstPubKey *ed25519.PublicKey, nextHandlers []peeringNetDynamicHandlerEntry, callHandlersAndSendFun func(nextHandlers []peeringNetDynamicHandlerEntry), log *logger.Logger, @@ -219,7 +220,7 @@ func (dcT *peeringNetDynamicHandlerDelayingChannel) handleSendMessage( } else { delay = time.Duration(fromMS) * time.Millisecond } - log.Debugf("Network delayed message %v -%v-> %v for %v", msg.from, msg.msg.MsgType, dstNetID, delay) + log.Debugf("Network delayed message %v -%v-> %v for %v", msg.from.String(), msg.msg.MsgType, dstPubKey.String(), delay) <-time.After(delay) } callHandlersAndSendFun(nextHandlers) @@ -227,22 +228,22 @@ func (dcT *peeringNetDynamicHandlerDelayingChannel) handleSendMessage( } type peeringNetDynamicHandlerPeerDisconnected struct { - peerName string + peerPubKey *ed25519.PublicKey } func (pdT *peeringNetDynamicHandlerPeerDisconnected) handleSendMessage( msg *peeringMsg, - dstNetID string, + dstPubKey *ed25519.PublicKey, nextHandlers []peeringNetDynamicHandlerEntry, callHandlersAndSendFun func(nextHandlers []peeringNetDynamicHandlerEntry), log *logger.Logger, ) { - if dstNetID == pdT.peerName { - log.Debugf("Network dropped message %v -%v-> %v, because destination is disconnected", msg.from, msg.msg.MsgType, dstNetID) + if *dstPubKey == *pdT.peerPubKey { + log.Debugf("Network dropped message %v -%v-> %v, because destination is disconnected", msg.from.String(), msg.msg.MsgType, dstPubKey.String()) return } - if msg.from == pdT.peerName { - log.Debugf("Network dropped message %v -%v-> %v, because source is disconnected", msg.from, msg.msg.MsgType, dstNetID) + if *msg.from == *pdT.peerPubKey { + log.Debugf("Network dropped message %v -%v-> %v, because source is disconnected", msg.from.String(), msg.msg.MsgType, dstPubKey.String()) return } callHandlersAndSendFun(nextHandlers) diff --git a/packages/testutil/peeringNetBehaviourDynamic_test.go b/packages/testutil/peeringNetBehaviourDynamic_test.go index c54c10a920..d54c9aba1f 100644 --- a/packages/testutil/peeringNetBehaviourDynamic_test.go +++ b/packages/testutil/peeringNetBehaviourDynamic_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/testutil/testlogger" "github.com/stretchr/testify/require" @@ -22,11 +23,14 @@ func TestPeeringNetDynamicReliable(t *testing.T) { } doneCh <- true }() - someNode := peeringNode{netID: "src"} + // peerNetI, peerIdentities := testpeers.SetupKeys(2) + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} // // Run the test. behavior := NewPeeringNetDynamic(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)) - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 10; i++ { sendMessage(&someNode, inCh) } @@ -42,14 +46,16 @@ func TestPeeringNetDynamicUnreliable(t *testing.T) { stopCh := make(chan bool) durations := make([]time.Duration, 0) go testRecvLoop(outCh, &durations, stopCh) - someNode := peeringNode{netID: "src"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} // // Run the test. behavior := NewPeeringNetDynamic(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)). WithLosingChannel(nil, 50). WithRepeatingChannel(nil, 50). WithDelayingChannel(nil, 50*time.Millisecond, 100*time.Millisecond) - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 1000; i++ { sendMessage(&someNode, inCh) } @@ -77,11 +83,13 @@ func TestPeeringNetDynamicChanging(t *testing.T) { stopCh := make(chan bool) durations := make([]time.Duration, 0) go testRecvLoop(outCh, &durations, stopCh) - someNode := peeringNode{netID: "src"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} // // Run the test. behavior := NewPeeringNetDynamic(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)) - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 100; i++ { sendMessage(&someNode, inCh) } @@ -140,11 +148,13 @@ func TestPeeringNetDynamicLosingChannel(t *testing.T) { stopCh := make(chan bool) durations := make([]time.Duration, 0) go testRecvLoop(outCh, &durations, stopCh) - someNode := peeringNode{netID: "src"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} // // Run the test. behavior := NewPeeringNetDynamic(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)).WithLosingChannel(nil, 50) - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 1000; i++ { sendMessage(&someNode, inCh) } @@ -163,11 +173,13 @@ func TestPeeringNetDynamicRepeatingChannel(t *testing.T) { stopCh := make(chan bool) durations := make([]time.Duration, 0) go testRecvLoop(outCh, &durations, stopCh) - someNode := peeringNode{netID: "src"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} // // Run the test. behavior := NewPeeringNetDynamic(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)).WithRepeatingChannel(nil, 150) - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 1000; i++ { sendMessage(&someNode, inCh) } @@ -186,11 +198,13 @@ func TestPeeringNetDynamicDelayingChannel(t *testing.T) { stopCh := make(chan bool) durations := make([]time.Duration, 0) go testRecvLoop(outCh, &durations, stopCh) - someNode := peeringNode{netID: "src"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} // // Run the test. behavior := NewPeeringNetDynamic(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)).WithDelayingChannel(nil, 25*time.Millisecond, 75*time.Millisecond) - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 100; i++ { sendMessage(&someNode, inCh) } @@ -213,13 +227,16 @@ func TestPeeringNetDynamicPeerDisconnected(t *testing.T) { durationsD := make([]time.Duration, 0) go testRecvLoop(outCh, &durations, stopCh) go testRecvLoop(outChD, &durationsD, stopCh) - connectedNode := peeringNode{netID: "src"} - disconnectedNode := peeringNode{netID: "disconnected"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + disPeerIdentity := ed25519.GenerateKeyPair() + connectedNode := peeringNode{netID: "src", identity: &srcPeerIdentity} + disconnectedNode := peeringNode{netID: "disconnected", identity: &disPeerIdentity} // // Run the test. - behavior := NewPeeringNetDynamic(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)).WithPeerDisconnected(nil, "disconnected") - behavior.AddLink(inCh, outCh, "dst") - behavior.AddLink(inChD, outChD, "disconnected") + behavior := NewPeeringNetDynamic(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)).WithPeerDisconnected(nil, &disPeerIdentity.PublicKey) + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) + behavior.AddLink(inChD, outChD, &disPeerIdentity.PublicKey) for i := 0; i < 100; i++ { sendMessage(&connectedNode, inCh) // Will be received sendMessage(&connectedNode, inChD) // Won't be received - destination is disconnected @@ -256,7 +273,7 @@ func averageDuration(durations []time.Duration) int64 { func sendMessage(from *peeringNode, inCh chan *peeringMsg) { inCh <- &peeringMsg{ - from: from.netID, + from: &from.identity.PublicKey, timestamp: time.Now().UnixNano(), } } diff --git a/packages/testutil/peeringNetBehaviour_test.go b/packages/testutil/peeringNetBehaviour_test.go index bd50d544a3..cf5d760f6d 100644 --- a/packages/testutil/peeringNetBehaviour_test.go +++ b/packages/testutil/peeringNetBehaviour_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/testutil/testlogger" "github.com/stretchr/testify/require" @@ -22,11 +23,13 @@ func TestPeeringNetReliable(t *testing.T) { } doneCh <- true }() - someNode := peeringNode{netID: "src"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} behavior := NewPeeringNetReliable(testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)) - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 10; i++ { - inCh <- &peeringMsg{from: someNode.netID} + inCh <- &peeringMsg{from: &someNode.identity.PublicKey} } <-doneCh behavior.Close() @@ -54,11 +57,13 @@ func TestPeeringNetUnreliable(t *testing.T) { }() // // Run the test. - someNode := peeringNode{netID: "src"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} behavior := NewPeeringNetUnreliable(50, 50, 50*time.Millisecond, 100*time.Millisecond, testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)) - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 1000; i++ { - inCh <- &peeringMsg{from: someNode.netID} + inCh <- &peeringMsg{from: &someNode.identity.PublicKey} } time.Sleep(500 * time.Millisecond) // @@ -102,11 +107,13 @@ func TestPeeringNetGoodQuality(t *testing.T) { }() // // Run the test. - someNode := peeringNode{netID: "src"} + srcPeerIdentity := ed25519.GenerateKeyPair() + dstPeerIdentity := ed25519.GenerateKeyPair() + someNode := peeringNode{netID: "src", identity: &srcPeerIdentity} behavior := NewPeeringNetUnreliable(100, 0, 0*time.Microsecond, 0*time.Millisecond, testlogger.WithLevel(testlogger.NewLogger(t), logger.LevelError, false)) // NOTE: No drops, duplicates, delays. - behavior.AddLink(inCh, outCh, "dst") + behavior.AddLink(inCh, outCh, &dstPeerIdentity.PublicKey) for i := 0; i < 1000; i++ { - inCh <- &peeringMsg{from: someNode.netID} + inCh <- &peeringMsg{from: &someNode.identity.PublicKey} } time.Sleep(500 * time.Millisecond) // diff --git a/packages/testutil/peeringNetworkProvider.go b/packages/testutil/peeringNetworkProvider.go index 2ad8e92466..395c341d8b 100644 --- a/packages/testutil/peeringNetworkProvider.go +++ b/packages/testutil/peeringNetworkProvider.go @@ -26,17 +26,6 @@ type PeeringNetwork struct { log *logger.Logger } -// NewPeeringNetworkForLocs creates a test network with new keys, etc. -func NewPeeringNetworkForLocs(peerNetIDs []string, bufSize int, log *logger.Logger) *PeeringNetwork { - identities := make([]*ed25519.KeyPair, len(peerNetIDs)) - for i := range identities { - nodeIdentity := ed25519.GenerateKeyPair() - identities[i] = &nodeIdentity - } - behavior := NewPeeringNetReliable(log) - return NewPeeringNetwork(peerNetIDs, identities, bufSize, behavior, log) -} - // NewPeeringNetwork creates new test network, it can then be used to create network nodes. func NewPeeringNetwork( netIDs []string, @@ -72,9 +61,9 @@ func (p *PeeringNetwork) NetworkProviders() []peering.NetworkProvider { return cp } -func (p *PeeringNetwork) nodeByNetID(nodeNetID string) *peeringNode { +func (p *PeeringNetwork) nodeByPubKey(nodePubKey *ed25519.PublicKey) *peeringNode { for i := range p.nodes { - if p.nodes[i].netID == nodeNetID { + if p.nodes[i].identity.PublicKey == *nodePubKey { return p.nodes[i] } } @@ -108,7 +97,7 @@ type peeringNode struct { } type peeringMsg struct { - from string + from *ed25519.PublicKey msg peering.PeerMessageData timestamp int64 } @@ -133,7 +122,7 @@ func newPeeringNode(netID string, identity *ed25519.KeyPair, network *PeeringNet network: network, log: network.log.With("loc", netID), } - network.behavior.AddLink(sendCh, recvCh, netID) + network.behavior.AddLink(sendCh, recvCh, &identity.PublicKey) go n.recvLoop() return &n } @@ -145,14 +134,14 @@ func (n *peeringNode) recvLoop() { if cb.peeringID.String() == msgPeeringID && cb.receiver == pm.msg.MsgReceiver { cb.callback(&peering.PeerMessageIn{ PeerMessageData: pm.msg, - SenderNetID: pm.from, + SenderPubKey: pm.from, }) } } } } -func (n *peeringNode) sendMsg(from string, msg *peering.PeerMessageData) { +func (n *peeringNode) sendMsg(from *ed25519.PublicKey, msg *peering.PeerMessageData) { n.sendCh <- &peeringMsg{ from: from, msg: *msg, @@ -199,22 +188,30 @@ func (p *peeringNetworkProvider) Self() peering.PeerSender { return newPeeringSender(p.self, p) } -// Group implements peering.NetworkProvider. -func (p *peeringNetworkProvider) PeerGroup(peeringID peering.PeeringID, peerAddrs []string) (peering.GroupProvider, error) { - peers := make([]peering.PeerSender, len(peerAddrs)) - for i := range peerAddrs { - n := p.network.nodeByNetID(peerAddrs[i]) +// PeerGroup implements peering.NetworkProvider. +func (p *peeringNetworkProvider) PeerGroup(peeringID peering.PeeringID, peerPubKeys []*ed25519.PublicKey) (peering.GroupProvider, error) { + peers := make([]peering.PeerSender, len(peerPubKeys)) + for i := range peerPubKeys { + n := p.network.nodeByPubKey(peerPubKeys[i]) if n == nil { - return nil, errors.New("unknown_node_location") + return nil, errors.New("unknown node location") } peers[i] = p.senders[i] } return group.NewPeeringGroupProvider(p, peeringID, peers, p.network.log) } -// Domain creates peering.PeerDomainProvider. -func (p *peeringNetworkProvider) PeerDomain(peeringID peering.PeeringID, peerNetIDs []string) (peering.PeerDomainProvider, error) { - return domain.NewPeerDomainByNetIDs(p, peeringID, peerNetIDs, p.network.log) +// PeerDomain creates peering.PeerDomainProvider. +func (p *peeringNetworkProvider) PeerDomain(peeringID peering.PeeringID, peerPubKeys []*ed25519.PublicKey) (peering.PeerDomainProvider, error) { + peers := make([]peering.PeerSender, len(peerPubKeys)) + for i := range peerPubKeys { + n := p.network.nodeByPubKey(peerPubKeys[i]) + if n == nil { + return nil, errors.New("unknown node pub key") + } + peers[i] = p.senders[i] + } + return domain.NewPeerDomain(p, peeringID, peers, p.network.log), nil } // Attach implements peering.NetworkProvider. @@ -237,8 +234,8 @@ func (p *peeringNetworkProvider) Detach(attachID interface{}) { // Detach is not important in tests. } -func (p *peeringNetworkProvider) SendMsgByNetID(netID string, msg *peering.PeerMessageData) { - s, err := p.PeerByNetID(netID) +func (p *peeringNetworkProvider) SendMsgByPubKey(peerPubKey *ed25519.PublicKey, msg *peering.PeerMessageData) { + s, err := p.PeerByPubKey(peerPubKey) if err == nil { s.SendMsg(msg) } @@ -310,7 +307,7 @@ func (p *peeringSender) PubKey() *ed25519.PublicKey { // Send implements peering.PeerSender. func (p *peeringSender) SendMsg(msg *peering.PeerMessageData) { - p.node.sendMsg(p.netProvider.self.netID, msg) + p.node.sendMsg(&p.netProvider.self.identity.PublicKey, msg) } // IsAlive implements peering.PeerSender. @@ -337,3 +334,8 @@ func (p *peeringSender) NumUsers() int { func (p *peeringSender) Close() { // Not needed in tests. } + +// Status implements peering.PeerSender. +func (p *peeringSender) Status() peering.PeerStatusProvider { + return p +} diff --git a/packages/testutil/peeringNetworkProvider_test.go b/packages/testutil/peeringNetworkProvider_test.go index 47229ac676..a609bb58a7 100644 --- a/packages/testutil/peeringNetworkProvider_test.go +++ b/packages/testutil/peeringNetworkProvider_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "github.com/iotaledger/wasp/packages/testutil/testlogger" - "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/testutil" + "github.com/iotaledger/wasp/packages/testutil/testlogger" + "github.com/iotaledger/wasp/packages/testutil/testpeers" ) func TestFakeNetwork(t *testing.T) { @@ -20,7 +20,8 @@ func TestFakeNetwork(t *testing.T) { chain1 := peering.RandomPeeringID() chain2 := peering.RandomPeeringID() receiver := byte(0) - network := testutil.NewPeeringNetworkForLocs([]string{"a", "b", "c"}, 100, log) + peerNetIDs, nodeIdentities := testpeers.SetupKeys(3) + network := testutil.NewPeeringNetwork(peerNetIDs, nodeIdentities, 100, testutil.NewPeeringNetReliable(log), log) var netProviders []peering.NetworkProvider = network.NetworkProviders() // // Node "a" listens for chain1 messages. @@ -30,8 +31,8 @@ func TestFakeNetwork(t *testing.T) { // // Node "b" sends some messages. var a, c peering.PeerSender - a, _ = netProviders[1].PeerByNetID("a") - c, _ = netProviders[1].PeerByNetID("c") + a, _ = netProviders[1].PeerByPubKey(&nodeIdentities[0].PublicKey) + c, _ = netProviders[1].PeerByPubKey(&nodeIdentities[2].PublicKey) a.SendMsg(&peering.PeerMessageData{PeeringID: chain1, MsgReceiver: receiver, MsgType: 1}) // Will be delivered. a.SendMsg(&peering.PeerMessageData{PeeringID: chain2, MsgReceiver: receiver, MsgType: 2}) // Will be dropped. a.SendMsg(&peering.PeerMessageData{PeeringID: chain1, MsgReceiver: byte(5), MsgType: 3}) // Will be dropped. diff --git a/packages/testutil/testchain/mock_chain_core.go b/packages/testutil/testchain/mock_chain_core.go index ca9e30f5b2..f1d1c97e17 100644 --- a/packages/testutil/testchain/mock_chain_core.go +++ b/packages/testutil/testchain/mock_chain_core.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package testchain import ( @@ -15,6 +18,7 @@ import ( "github.com/iotaledger/wasp/packages/iscp/request" "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/state" + "github.com/iotaledger/wasp/packages/vm/core/governance" "github.com/iotaledger/wasp/packages/vm/processors" "go.uber.org/atomic" ) @@ -223,3 +227,11 @@ func (m *MockedChainCore) OnMissingRequest(fun func(msg *messages.MissingRequest func (m *MockedChainCore) OnTimerTick(fun func(tick int)) { m.onTimerTick = fun } + +func (m *MockedChainCore) GetChainNodes() []peering.PeerStatusProvider { + panic("not implemented MockedChainCore::GetChainNodes") +} + +func (m *MockedChainCore) GetCandidateNodes() []*governance.AccessNodeInfo { + panic("not implemented MockedChainCore::GetCandidateNodes") +} diff --git a/packages/testutil/testchain/mock_registry.go b/packages/testutil/testchain/mock_registry.go deleted file mode 100644 index bc9eb83aaa..0000000000 --- a/packages/testutil/testchain/mock_registry.go +++ /dev/null @@ -1,25 +0,0 @@ -package testchain - -import ( - "github.com/iotaledger/goshimmer/packages/ledgerstate" - "github.com/iotaledger/wasp/packages/registry" -) - -type MockedCommitteeRegistry struct { - validators []string -} - -func NewMockedCommitteeRegistry(validators []string) *MockedCommitteeRegistry { - return &MockedCommitteeRegistry{validators} -} - -func (m *MockedCommitteeRegistry) GetCommitteeRecord(addr ledgerstate.Address) (*registry.CommitteeRecord, error) { - return ®istry.CommitteeRecord{ - Address: addr, - Nodes: m.validators, - }, nil -} - -func (m *MockedCommitteeRegistry) SaveCommitteeRecord(rec *registry.CommitteeRecord) error { - panic("implement me") -} diff --git a/packages/testutil/testpeers/testkeys.go b/packages/testutil/testpeers/testkeys.go index dddd62e1e9..75ab0461f4 100644 --- a/packages/testutil/testpeers/testkeys.go +++ b/packages/testutil/testpeers/testkeys.go @@ -32,10 +32,10 @@ func SetupKeys(peerCount uint16) ([]string, []*ed25519.KeyPair) { return peerNetIDs, peerIdentities } -func PublicKeys(peerIdentities []*ed25519.KeyPair) []ed25519.PublicKey { - pubKeys := make([]ed25519.PublicKey, len(peerIdentities)) +func PublicKeys(peerIdentities []*ed25519.KeyPair) []*ed25519.PublicKey { + pubKeys := make([]*ed25519.PublicKey, len(peerIdentities)) for i := range pubKeys { - pubKeys[i] = peerIdentities[i].PublicKey + pubKeys[i] = &peerIdentities[i].PublicKey } return pubKeys } @@ -66,7 +66,6 @@ func SetupDkg( // // Initiate the key generation from some client node. dkShare, err := dkgNodes[0].GenerateDistributedKey( - peerNetIDs, PublicKeys(peerIdentities), threshold, 100*time.Second, diff --git a/packages/webapi/admapi/activatechain.go b/packages/webapi/admapi/activatechain.go index 5930bc07b1..a34e663ceb 100644 --- a/packages/webapi/admapi/activatechain.go +++ b/packages/webapi/admapi/activatechain.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package admapi import ( @@ -5,18 +8,35 @@ import ( "net/http" "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/packages/chains" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/metrics" + "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/registry" + "github.com/iotaledger/wasp/packages/tcrypto" + "github.com/iotaledger/wasp/packages/vm/core/governance" "github.com/iotaledger/wasp/packages/webapi/httperrors" + "github.com/iotaledger/wasp/packages/webapi/model" "github.com/iotaledger/wasp/packages/webapi/routes" "github.com/labstack/echo/v4" "github.com/pangpanglabs/echoswagger/v2" ) -func addChainEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Provider, chainsProvider chains.Provider, allMetrics *metrics.Metrics) { - c := &chainWebAPI{registryProvider, chainsProvider, allMetrics} +type chainWebAPI struct { + registry registry.Provider + chains chains.Provider + network peering.NetworkProvider + allMetrics *metrics.Metrics +} + +func addChainEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Provider, chainsProvider chains.Provider, network peering.NetworkProvider, allMetrics *metrics.Metrics) { + c := &chainWebAPI{ + registryProvider, + chainsProvider, + network, + allMetrics, + } adm.POST(routes.ActivateChain(":chainID"), c.handleActivateChain). AddParamPath("", "chainID", "ChainID (base58)"). @@ -25,12 +45,10 @@ func addChainEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Provi adm.POST(routes.DeactivateChain(":chainID"), c.handleDeactivateChain). AddParamPath("", "chainID", "ChainID (base58)"). SetSummary("Deactivate a chain") -} -type chainWebAPI struct { - registry registry.Provider - chains chains.Provider - allMetrics *metrics.Metrics + adm.GET(routes.GetChainInfo(":chainID"), c.handleGetChainInfo). + AddParamPath("", "chainID", "ChainID (base58)"). + SetSummary("Get basic chain info.") } func (w *chainWebAPI) handleActivateChain(c echo.Context) error { @@ -76,3 +94,150 @@ func (w *chainWebAPI) handleDeactivateChain(c echo.Context) error { return c.NoContent(http.StatusOK) } + +func (w *chainWebAPI) handleGetChainInfo(c echo.Context) error { + scAddress, err := ledgerstate.AddressFromBase58EncodedString(c.Param("chainID")) + if err != nil { + return httperrors.BadRequest(fmt.Sprintf("Invalid chain id: %s", c.Param("chainID"))) + } + chainID, err := iscp.ChainIDFromAddress(scAddress) + if err != nil { + return err + } + + chain := w.chains().Get(chainID, true) + committeeInfo := chain.GetCommitteeInfo() + chainRecord, err := w.registry().GetChainRecordByChainID(chainID) + if err != nil { + return err + } + dkShare, err := w.registry().LoadDKShare(committeeInfo.Address) + if err != nil { + return err + } + + chainNodes := chain.GetChainNodes() + peeringStatus := make(map[ed25519.PublicKey]peering.PeerStatusProvider) + for _, n := range w.network.PeerStatus() { + peeringStatus[*n.PubKey()] = n + } + candidateNodes := make(map[ed25519.PublicKey]*governance.AccessNodeInfo) + for _, n := range chain.GetCandidateNodes() { + pubKey, _, err := ed25519.PublicKeyFromBytes(n.NodePubKey) + if err != nil { + return err + } + candidateNodes[pubKey] = n + } + + inChainNodes := make(map[ed25519.PublicKey]bool) + + // + // Committee nodes. + cmtNodes := makeCmtNodes(dkShare, peeringStatus, candidateNodes, inChainNodes) + + // + // Access nodes: accepted as access nodes and not included in the committee. + acnNodes := makeAcnNodes(dkShare, chainNodes, peeringStatus, candidateNodes, inChainNodes) + + // + // Candidate nodes have suplied applications, but are not included + // in the committee and to the set of the access nodes. + cndNodes, err := makeCndNodes(peeringStatus, candidateNodes, inChainNodes) + if err != nil { + return err + } + + res := model.ChainInfo{ + ChainID: model.ChainID(chainID.Base58()), + Active: chainRecord.Active, + StateAddress: model.NewAddress(committeeInfo.Address), + CommitteeNodes: cmtNodes, + AccessNodes: acnNodes, + CandidateNodes: cndNodes, + } + + return c.JSON(http.StatusOK, res) +} + +func makeCmtNodes( + dkShare *tcrypto.DKShare, + peeringStatus map[ed25519.PublicKey]peering.PeerStatusProvider, + candidateNodes map[ed25519.PublicKey]*governance.AccessNodeInfo, + inChainNodes map[ed25519.PublicKey]bool, +) []*model.ChainNodeStatus { + cmtNodes := make([]*model.ChainNodeStatus, 0) + for _, cmtNodePubKey := range dkShare.NodePubKeys { + cmtNodes = append(cmtNodes, makeChainNodeStatus(cmtNodePubKey, peeringStatus, candidateNodes)) + inChainNodes[*cmtNodePubKey] = true + } + return cmtNodes +} + +func makeAcnNodes( + dkShare *tcrypto.DKShare, + chainNodes []peering.PeerStatusProvider, + peeringStatus map[ed25519.PublicKey]peering.PeerStatusProvider, + candidateNodes map[ed25519.PublicKey]*governance.AccessNodeInfo, + inChainNodes map[ed25519.PublicKey]bool, +) []*model.ChainNodeStatus { + acnNodes := make([]*model.ChainNodeStatus, 0) + for _, chainNode := range chainNodes { + acnPubKey := chainNode.PubKey() + skip := false + for _, cmtNodePubKey := range dkShare.NodePubKeys { + if *acnPubKey == *cmtNodePubKey { + skip = true + break + } + } + if skip { + continue + } + acnNodes = append(acnNodes, makeChainNodeStatus(acnPubKey, peeringStatus, candidateNodes)) + inChainNodes[*acnPubKey] = true + } + return acnNodes +} + +func makeCndNodes( + peeringStatus map[ed25519.PublicKey]peering.PeerStatusProvider, + candidateNodes map[ed25519.PublicKey]*governance.AccessNodeInfo, + inChainNodes map[ed25519.PublicKey]bool, +) ([]*model.ChainNodeStatus, error) { + cndNodes := make([]*model.ChainNodeStatus, 0) + for _, c := range candidateNodes { + pubKey, _, err := ed25519.PublicKeyFromBytes(c.NodePubKey) + if err != nil { + return nil, err + } + if _, ok := inChainNodes[pubKey]; ok { + continue // Only include unused candidates here. + } + cndNodes = append(cndNodes, makeChainNodeStatus(&pubKey, peeringStatus, candidateNodes)) + } + return cndNodes, nil +} + +func makeChainNodeStatus( + pubKey *ed25519.PublicKey, + peeringStatus map[ed25519.PublicKey]peering.PeerStatusProvider, + candidateNodes map[ed25519.PublicKey]*governance.AccessNodeInfo, +) *model.ChainNodeStatus { + cns := model.ChainNodeStatus{ + Node: model.PeeringNodeStatus{ + PubKey: pubKey.String(), + }, + } + if n, ok := peeringStatus[*pubKey]; ok { + cns.Node.NetID = n.NetID() + cns.Node.IsAlive = n.IsAlive() + cns.Node.NumUsers = n.NumUsers() + } + if n, ok := candidateNodes[*pubKey]; ok { + cns.ForCommittee = n.ForCommittee + cns.ForAccess = true + cns.AccessAPI = n.AccessAPI + } + return &cns +} diff --git a/packages/webapi/admapi/committeerecord.go b/packages/webapi/admapi/committeerecord.go deleted file mode 100644 index 998f6bb2a7..0000000000 --- a/packages/webapi/admapi/committeerecord.go +++ /dev/null @@ -1,112 +0,0 @@ -package admapi - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/iotaledger/goshimmer/packages/ledgerstate" - "github.com/iotaledger/wasp/packages/chains" - "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/registry" - "github.com/iotaledger/wasp/packages/webapi/httperrors" - "github.com/iotaledger/wasp/packages/webapi/model" - "github.com/iotaledger/wasp/packages/webapi/routes" - "github.com/labstack/echo/v4" - "github.com/pangpanglabs/echoswagger/v2" -) - -func addCommitteeRecordEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Provider, chainsProvider chains.Provider) { - rnd1 := iscp.RandomChainID() - example := model.CommitteeRecord{ - Address: model.NewAddress(rnd1.AsAddress()), - Nodes: []string{"wasp1.example.org:4000", "wasp2.example.org:4000", "wasp3.example.org:4000"}, - } - - s := &committeeRecordService{registryProvider, chainsProvider} - - adm.POST(routes.PutCommitteeRecord(), s.handlePutCommitteeRecord). - SetSummary("Create a new committee record"). - AddParamBody(example, "Record", "Committee record", true) - - adm.GET(routes.GetCommitteeRecord(":address"), s.handleGetCommitteeRecord). - SetSummary("Find the committee record for the given address"). - AddParamPath("", "address", "Address (base58)"). - AddResponse(http.StatusOK, "Committee Record", example, nil) - - adm.GET(routes.GetCommitteeForChain(":chainID"), s.handleGetCommitteeForChain). - SetSummary("Find the committee record that manages the given chain"). - AddParamPath("", "chainID", "ChainID (base58)"). - AddResponse(http.StatusOK, "Committee Record", example, nil) -} - -type committeeRecordService struct { - registry registry.Provider - chains chains.Provider -} - -func (s *committeeRecordService) handlePutCommitteeRecord(c echo.Context) error { - var req model.CommitteeRecord - - if err := c.Bind(&req); err != nil { - return httperrors.BadRequest("Invalid request body") - } - - cr := req.Record() - - defaultRegistry := s.registry() - bd2, err := defaultRegistry.GetCommitteeRecord(cr.Address) - if err != nil { - return err - } - if bd2 != nil { - return httperrors.Conflict(fmt.Sprintf("Record already exists: %s", cr.Address.Base58())) - } - // TODO if I am not among committee nodes, should not save - if err := defaultRegistry.SaveCommitteeRecord(cr); err != nil { - return err - } - - log.Infof("Committee record saved. Address: %s", cr.String()) - - return c.NoContent(http.StatusCreated) -} - -func (s *committeeRecordService) handleGetCommitteeRecord(c echo.Context) error { - address, err := ledgerstate.AddressFromBase58EncodedString(c.Param("address")) - if err != nil { - return httperrors.BadRequest(err.Error()) - } - cr, err := s.registry().GetCommitteeRecord(address) - if err != nil { - return err - } - if cr == nil { - return httperrors.NotFound(fmt.Sprintf("Record not found: %s", address)) - } - return c.JSON(http.StatusOK, model.NewCommitteeRecord(cr)) -} - -func (s *committeeRecordService) handleGetCommitteeForChain(c echo.Context) error { - chainID, err := iscp.ChainIDFromBase58(c.Param("chainID")) - if err != nil { - return httperrors.BadRequest(err.Error()) - } - includeDeactivated, _ := strconv.ParseBool(c.QueryParam("includeDeactivated")) - chain := s.chains().Get(chainID, includeDeactivated) - if chain == nil { - return httperrors.NotFound(fmt.Sprintf("Active chain %s not found", chainID)) - } - committeeInfo := chain.GetCommitteeInfo() - if committeeInfo == nil { - return httperrors.NotFound(fmt.Sprintf("Committee info for chain %s is not available", chainID)) - } - cr, err := s.registry().GetCommitteeRecord(committeeInfo.Address) - if err != nil { - return err - } - if cr == nil { - return httperrors.NotFound(fmt.Sprintf("Committee record not found for address: %s", committeeInfo.Address.Base58())) - } - return c.JSON(http.StatusOK, model.NewCommitteeRecord(cr)) -} diff --git a/packages/webapi/admapi/dkshares.go b/packages/webapi/admapi/dkshares.go index 12e14778ed..220c7a78bb 100644 --- a/packages/webapi/admapi/dkshares.go +++ b/packages/webapi/admapi/dkshares.go @@ -26,7 +26,6 @@ import ( func addDKSharesEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Provider, nodeProvider dkg.NodeProvider) { requestExample := model.DKSharesPostRequest{ - PeerNetIDs: []string{"wasp1:4000", "wasp2:4000", "wasp3:4000", "wasp4:4000"}, PeerPubKeys: []string{base64.StdEncoding.EncodeToString([]byte("key"))}, Threshold: 3, TimeoutMS: 10000, @@ -36,11 +35,12 @@ func addDKSharesEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Pr Address: addr1.Base58(), SharedPubKey: base64.StdEncoding.EncodeToString([]byte("key")), PubKeyShares: []string{base64.StdEncoding.EncodeToString([]byte("key"))}, + PeerPubKeys: []string{base64.StdEncoding.EncodeToString([]byte("key"))}, Threshold: 3, PeerIndex: nil, } - s := &dkSharesService{registryProvider, nodeProvider} + s := &dkSharesService{registry: registryProvider, dkgNode: nodeProvider} adm.POST(routes.DKSharesPost(), s.handleDKSharesPost). AddParamBody(requestExample, "DKSharesPostRequest", "Request parameters", true). @@ -66,23 +66,24 @@ func (s *dkSharesService) handleDKSharesPost(c echo.Context) error { return httperrors.BadRequest("Invalid request body.") } - if req.PeerPubKeys != nil && len(req.PeerNetIDs) != len(req.PeerPubKeys) { - return httperrors.BadRequest("Inconsistent PeerNetIDs and PeerPubKeys.") + if req.PeerPubKeys == nil || len(req.PeerPubKeys) < 1 { + return httperrors.BadRequest("PeerPubKeys are mandatory") } - var peerPubKeys []ed25519.PublicKey + var peerPubKeys []*ed25519.PublicKey if req.PeerPubKeys != nil { - peerPubKeys = make([]ed25519.PublicKey, len(req.PeerPubKeys)) + peerPubKeys = make([]*ed25519.PublicKey, len(req.PeerPubKeys)) for i := range req.PeerPubKeys { - if peerPubKeys[i], err = ed25519.PublicKeyFromString(req.PeerPubKeys[i]); err != nil { + peerPubKey, err := ed25519.PublicKeyFromString(req.PeerPubKeys[i]) + if err != nil { return httperrors.BadRequest(fmt.Sprintf("Invalid PeerPubKeys[%v]=%v", i, req.PeerPubKeys[i])) } + peerPubKeys[i] = &peerPubKey } } var dkShare *tcrypto.DKShare dkShare, err = s.dkgNode().GenerateDistributedKey( - req.PeerNetIDs, peerPubKeys, req.Threshold, 1*time.Second, @@ -138,10 +139,16 @@ func makeDKSharesInfo(dkShare *tcrypto.DKShare) (*model.DKSharesInfo, error) { pubKeyShares[i] = base64.StdEncoding.EncodeToString(b) } + peerPubKeys := make([]string, len(dkShare.NodePubKeys)) + for i := range dkShare.NodePubKeys { + peerPubKeys[i] = base64.StdEncoding.EncodeToString(dkShare.NodePubKeys[i].Bytes()) + } + return &model.DKSharesInfo{ Address: dkShare.Address.Base58(), SharedPubKey: sharedPubKey, PubKeyShares: pubKeyShares, + PeerPubKeys: peerPubKeys, Threshold: dkShare.T, PeerIndex: dkShare.Index, }, nil diff --git a/packages/webapi/admapi/endpoints.go b/packages/webapi/admapi/endpoints.go index 29fd066b48..6042b8d401 100644 --- a/packages/webapi/admapi/endpoints.go +++ b/packages/webapi/admapi/endpoints.go @@ -40,8 +40,7 @@ func AddEndpoints( addShutdownEndpoint(adm, shutdown) addChainRecordEndpoints(adm, registryProvider) - addCommitteeRecordEndpoints(adm, registryProvider, chainsProvider) - addChainEndpoints(adm, registryProvider, chainsProvider, metrics) + addChainEndpoints(adm, registryProvider, chainsProvider, network, metrics) addDKSharesEndpoints(adm, registryProvider, nodeProvider) addPeeringEndpoints(adm, network, tnm) } diff --git a/packages/webapi/endpoints.go b/packages/webapi/endpoints.go index 24e4af5ea5..6dc8e2faba 100644 --- a/packages/webapi/endpoints.go +++ b/packages/webapi/endpoints.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package webapi import ( @@ -50,6 +53,7 @@ func Init( chainsProvider.ChainProvider(), webapiutil.GetAccountBalance, webapiutil.HasRequestBeenProcessed, + network.Self().PubKey(), time.Duration(parameters.GetInt(parameters.OffledgerAPICacheTTL))*time.Second, log, ) diff --git a/packages/webapi/model/chain_info.go b/packages/webapi/model/chain_info.go new file mode 100644 index 0000000000..76df19c84f --- /dev/null +++ b/packages/webapi/model/chain_info.go @@ -0,0 +1,20 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package model + +type ChainInfo struct { + ChainID ChainID `swagger:"desc(ChainID (base58-encoded))"` + Active bool `swagger:"desc(Whether or not the chain is active)"` + StateAddress Address `swagger:"desc(State address, if we are part of it.)"` + CommitteeNodes []*ChainNodeStatus `swagger:"desc(Committee nodes and their peering info.)"` + AccessNodes []*ChainNodeStatus `swagger:"desc(Access nodes and their peering info.)"` + CandidateNodes []*ChainNodeStatus `swagger:"desc(Candidate nodes and their peering info.)"` +} + +type ChainNodeStatus struct { + Node PeeringNodeStatus + ForCommittee bool + ForAccess bool + AccessAPI string +} diff --git a/packages/webapi/model/committee_record.go b/packages/webapi/model/committee_record.go deleted file mode 100644 index 0f1c297c09..0000000000 --- a/packages/webapi/model/committee_record.go +++ /dev/null @@ -1,22 +0,0 @@ -package model - -import "github.com/iotaledger/wasp/packages/registry" - -type CommitteeRecord struct { - Address Address `swagger:"desc(Committee address (base58-encoded))"` - Nodes []string `swagger:"desc(List of committee nodes (network IDs))"` -} - -func NewCommitteeRecord(bd *registry.CommitteeRecord) *CommitteeRecord { - return &CommitteeRecord{ - Address: NewAddress(bd.Address), - Nodes: bd.Nodes, - } -} - -func (bd *CommitteeRecord) Record() *registry.CommitteeRecord { - return ®istry.CommitteeRecord{ - Address: bd.Address.Address(), - Nodes: bd.Nodes, - } -} diff --git a/packages/webapi/model/dkshares.go b/packages/webapi/model/dkshares.go index d76f71cb8a..4e8388360b 100644 --- a/packages/webapi/model/dkshares.go +++ b/packages/webapi/model/dkshares.go @@ -5,7 +5,6 @@ package model // DKSharesPostRequest is a POST request for creating new DKShare. type DKSharesPostRequest struct { - PeerNetIDs []string `json:"peerNetIDs" swagger:"desc(NetIDs of the nodes sharing the key.)"` PeerPubKeys []string `json:"peerPubKeys" swagger:"desc(Optional, base64 encoded public keys of the peers generating the DKS.)"` Threshold uint16 `json:"threshold" swagger:"desc(Should be =< len(PeerPubKeys))"` TimeoutMS uint32 `json:"timeoutMS" swagger:"desc(Timeout in milliseconds.)"` @@ -16,6 +15,7 @@ type DKSharesInfo struct { Address string `json:"address" swagger:"desc(New generated shared address.)"` SharedPubKey string `json:"sharedPubKey" swagger:"desc(Shared public key (base64-encoded).)"` PubKeyShares []string `json:"pubKeyShares" swagger:"desc(Public key shares for all the peers (base64-encoded).)"` + PeerPubKeys []string `json:"peerPubKeys" swagger:"desc(Public keys of the nodes sharing the key (base64-encoded).)"` Threshold uint16 `json:"threshold"` PeerIndex *uint16 `json:"peerIndex" swagger:"desc(Index of the node returning the share, if it is a member of the sharing group.)"` } diff --git a/packages/webapi/request/request.go b/packages/webapi/request/request.go index 385fbb039a..a21626e87b 100644 --- a/packages/webapi/request/request.go +++ b/packages/webapi/request/request.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/wasp/packages/chain" @@ -33,6 +34,7 @@ func AddEndpoints( getChain chains.ChainProvider, getChainBalance getAccountBalanceFn, hasRequestBeenProcessed hasRequestBeenProcessedFn, + nodePubKey *ed25519.PublicKey, cacheTTL time.Duration, log *logger.Logger, ) { @@ -41,6 +43,7 @@ func AddEndpoints( getAccountBalance: getChainBalance, hasRequestBeenProcessed: hasRequestBeenProcessed, requestsCache: expiringcache.New(cacheTTL), + nodePubKey: nodePubKey, log: log, } server.POST(routes.NewRequest(":chainID"), instance.handleNewRequest). @@ -59,6 +62,7 @@ type offLedgerReqAPI struct { getAccountBalance getAccountBalanceFn hasRequestBeenProcessed hasRequestBeenProcessedFn requestsCache *expiringcache.ExpiringCache + nodePubKey *ed25519.PublicKey log *logger.Logger } @@ -120,7 +124,7 @@ func (o *offLedgerReqAPI) handleNewRequest(c echo.Context) error { ChainID: ch.ID(), Req: offLedgerReq, }, - SenderNetID: "", + SenderPubKey: o.nodePubKey, }) return c.NoContent(http.StatusAccepted) diff --git a/packages/webapi/routes/routes.go b/packages/webapi/routes/routes.go index 630e052cbd..67aeb9e673 100644 --- a/packages/webapi/routes/routes.go +++ b/packages/webapi/routes/routes.go @@ -35,6 +35,10 @@ func DeactivateChain(chainID string) string { return "/adm/chain/" + chainID + "/deactivate" } +func GetChainInfo(chainID string) string { + return "/adm/chain/" + chainID + "/info" +} + func ListChainRecords() string { return "/adm/chainrecords" } @@ -47,18 +51,6 @@ func GetChainRecord(chainID string) string { return "/adm/chainrecord/" + chainID } -func PutCommitteeRecord() string { - return "/adm/committeerecord" -} - -func GetCommitteeRecord(addr string) string { - return "/adm/committeerecord/" + addr -} - -func GetCommitteeForChain(chainID string) string { - return "/adm/chain/" + chainID + "/committeerecord" -} - func DKSharesPost() string { return "/adm/dks" } diff --git a/tools/cluster/cluster.go b/tools/cluster/cluster.go index ae88e61b2c..7c1925acfe 100644 --- a/tools/cluster/cluster.go +++ b/tools/cluster/cluster.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package cluster import ( @@ -102,10 +105,18 @@ func (clu *Cluster) RunDKG(committeeNodes []int, threshold uint16, timeout ...ti threshold = (uint16(len(committeeNodes))*2)/3 + 1 } apiHosts := clu.Config.APIHosts(committeeNodes) - peeringHosts := clu.Config.PeeringHosts(committeeNodes) + + peerPubKeys := make([]string, 0) + for _, i := range committeeNodes { + peeringNodeInfo, err := clu.WaspClient(i).GetPeeringSelf() + if err != nil { + return nil, err + } + peerPubKeys = append(peerPubKeys, peeringNodeInfo.PubKey) + } dkgInitiatorIndex := uint16(rand.Intn(len(apiHosts))) - return apilib.RunDKG(apiHosts, peeringHosts, threshold, dkgInitiatorIndex, timeout...) + return apilib.RunDKG(apiHosts, peerPubKeys, threshold, dkgInitiatorIndex, timeout...) } func (clu *Cluster) DeployChainWithDKG(description string, allPeers, committeeNodes []int, quorum uint16) (*Chain, error) { @@ -139,18 +150,25 @@ func (clu *Cluster) DeployChain(description string, allPeers, committeeNodes []i return nil, xerrors.Errorf("DeployChain: %w", err) } + committeePubKeys := make([]string, 0) + for _, i := range chain.CommitteeNodes { + peeringNode, err := clu.WaspClient(i).GetPeeringSelf() + if err != nil { + return nil, err + } + committeePubKeys = append(committeePubKeys, peeringNode.PubKey) + } + chainid, err := apilib.DeployChain(apilib.CreateChainParams{ - Node: clu.GoshimmerClient(), - AllAPIHosts: chain.AllAPIHosts(), - AllPeeringHosts: chain.AllPeeringHosts(), - CommitteeAPIHosts: chain.CommitteeAPIHosts(), - CommitteePeeringHosts: chain.CommitteePeeringHosts(), - N: uint16(len(committeeNodes)), - T: quorum, - OriginatorKeyPair: chain.OriginatorKeyPair(), - Description: description, - Textout: os.Stdout, - Prefix: "[cluster] ", + Node: clu.GoshimmerClient(), + CommitteeAPIHosts: chain.CommitteeAPIHosts(), + CommitteePubKeys: committeePubKeys, + N: uint16(len(committeeNodes)), + T: quorum, + OriginatorKeyPair: chain.OriginatorKeyPair(), + Description: description, + Textout: os.Stdout, + Prefix: "[cluster] ", }, stateAddr) if err != nil { return nil, xerrors.Errorf("DeployChain: %w", err) diff --git a/tools/cluster/tests/advanced_inccounter_test.go b/tools/cluster/tests/advanced_inccounter_test.go index a91a24f3ca..4ba8914d1f 100644 --- a/tools/cluster/tests/advanced_inccounter_test.go +++ b/tools/cluster/tests/advanced_inccounter_test.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package tests import ( @@ -37,7 +40,7 @@ func setupAdvancedInccounterTest(t *testing.T, clusterSize int, committee []int) t.Logf("generated state address: %s", addr.Base58()) - chain, err := clu.DeployChain("chain", clu.Config.AllNodes(), committee, quorum, addr) + chain, err := clu.DeployChain("chain", clu.Config.AllNodes(), committee, quorum, addr) // TODO: XXX: KP: AllNodes is used? require.NoError(t, err) t.Logf("deployed chainID: %s", chain.ChainID.Base58()) diff --git a/tools/wasp-cli/chain/activate.go b/tools/wasp-cli/chain/activate.go index dcaa785003..e765e3a0f9 100644 --- a/tools/wasp-cli/chain/activate.go +++ b/tools/wasp-cli/chain/activate.go @@ -7,18 +7,18 @@ import ( var activateCmd = &cobra.Command{ Use: "activate", - Short: "Activate the chain", + Short: "Activate the chain on this node", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - log.Check(MultiClient().ActivateChain(GetCurrentChainID())) + log.Check(Client().WaspClient.ActivateChain(GetCurrentChainID())) }, } var deactivateCmd = &cobra.Command{ Use: "deactivate", - Short: "Deactivate the chain", + Short: "Deactivate the chain on this node", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - log.Check(MultiClient().DeactivateChain(GetCurrentChainID())) + log.Check(Client().WaspClient.DeactivateChain(GetCurrentChainID())) }, } diff --git a/tools/wasp-cli/chain/client.go b/tools/wasp-cli/chain/client.go index 5b129d4e10..be437b0fc7 100644 --- a/tools/wasp-cli/chain/client.go +++ b/tools/wasp-cli/chain/client.go @@ -1,8 +1,10 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package chain import ( "github.com/iotaledger/wasp/client/chainclient" - "github.com/iotaledger/wasp/client/multiclient" "github.com/iotaledger/wasp/client/scclient" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/tools/wasp-cli/config" @@ -18,10 +20,6 @@ func Client() *chainclient.Client { ) } -func MultiClient() *multiclient.MultiClient { - return multiclient.New(config.CommitteeAPI(chainCommittee())) -} - func SCClient(contractHname iscp.Hname) *scclient.SCClient { return scclient.New(Client(), contractHname) } diff --git a/tools/wasp-cli/chain/committee.go b/tools/wasp-cli/chain/committee.go deleted file mode 100644 index 4ca9c60a20..0000000000 --- a/tools/wasp-cli/chain/committee.go +++ /dev/null @@ -1,18 +0,0 @@ -package chain - -import ( - "github.com/iotaledger/wasp/tools/wasp-cli/config" - "github.com/iotaledger/wasp/tools/wasp-cli/log" -) - -func chainCommittee() []int { - chainID := GetCurrentChainID() - committee, err := config.WaspClient().GetCommitteeForChain(chainID) - log.Check(err) - - r := []int{} - for _, peering := range committee.Nodes { - r = append(r, config.FindNodeBy(config.HostKindPeering, peering)) - } - return r -} diff --git a/tools/wasp-cli/chain/deploy.go b/tools/wasp-cli/chain/deploy.go index 15b55329e8..6788b3ad07 100644 --- a/tools/wasp-cli/chain/deploy.go +++ b/tools/wasp-cli/chain/deploy.go @@ -1,8 +1,12 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package chain import ( "os" + "github.com/iotaledger/wasp/client" "github.com/iotaledger/wasp/packages/apilib" "github.com/iotaledger/wasp/tools/wasp-cli/config" "github.com/iotaledger/wasp/tools/wasp-cli/log" @@ -12,7 +16,6 @@ import ( func deployCmd() *cobra.Command { var ( - peers []int committee []int quorum int description string @@ -25,29 +28,26 @@ func deployCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { alias := GetChainAlias() - if peers == nil { - if committee != nil { - peers = committee - } else { - peers = []int{0, 1, 2, 3} - } + if committee == nil { + committee = []int{0, 1, 2, 3} } - if committee == nil { - committee = peers + committeePubKeys := make([]string, 0) + for _, api := range config.CommitteeAPI(committee) { + peerInfo, err := client.NewWaspClient(api).GetPeeringSelf() + log.Check(err) + committeePubKeys = append(committeePubKeys, peerInfo.PubKey) } chainid, _, err := apilib.DeployChainWithDKG(apilib.CreateChainParams{ - Node: config.GoshimmerClient(), - AllAPIHosts: config.CommitteeAPI(peers), - AllPeeringHosts: config.CommitteePeering(peers), - CommitteeAPIHosts: config.CommitteeAPI(committee), - CommitteePeeringHosts: config.CommitteePeering(committee), - N: uint16(len(committee)), - T: uint16(quorum), - OriginatorKeyPair: wallet.Load().KeyPair(), - Description: description, - Textout: os.Stdout, + Node: config.GoshimmerClient(), + CommitteeAPIHosts: config.CommitteeAPI(committee), + CommitteePubKeys: committeePubKeys, + N: uint16(len(committee)), + T: uint16(quorum), + OriginatorKeyPair: wallet.Load().KeyPair(), + Description: description, + Textout: os.Stdout, }) log.Check(err) @@ -55,8 +55,7 @@ func deployCmd() *cobra.Command { }, } - cmd.Flags().IntSliceVarP(&peers, "peers", "", nil, "indices of peer nodes (default: 0,1,2,3)") - cmd.Flags().IntSliceVarP(&committee, "committee", "", nil, "subset of peers acting as committee nodes (default: same as peers)") + cmd.Flags().IntSliceVarP(&committee, "committee", "", nil, "peers acting as committee nodes (default: 0,1,2,3)") cmd.Flags().IntVarP(&quorum, "quorum", "", 3, "quorum") cmd.Flags().StringVarP(&description, "description", "", "", "description") return cmd diff --git a/tools/wasp-cli/chain/info.go b/tools/wasp-cli/chain/info.go index a3bfb59b15..6922f7f53d 100644 --- a/tools/wasp-cli/chain/info.go +++ b/tools/wasp-cli/chain/info.go @@ -1,10 +1,16 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package chain import ( + "strconv" + "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/packages/kv/collections" "github.com/iotaledger/wasp/packages/vm/core/governance" "github.com/iotaledger/wasp/packages/vm/core/root" + "github.com/iotaledger/wasp/packages/webapi/model" "github.com/iotaledger/wasp/tools/wasp-cli/config" "github.com/iotaledger/wasp/tools/wasp-cli/log" "github.com/spf13/cobra" @@ -15,17 +21,41 @@ var infoCmd = &cobra.Command{ Short: "Show information about the chain", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - chain, err := config.WaspClient().GetChainRecord(GetCurrentChainID()) + chainInfo, err := config.WaspClient().GetChainInfo(GetCurrentChainID()) log.Check(err) - committee, err := config.WaspClient().GetCommitteeForChain(chain.ChainID) - log.Check(err) + printNodesRowHdr := []string{"PubKey", "NetID", "Alive", "Committee", "Access", "AccessAPI"} + printNodesRowFmt := func(n *model.ChainNodeStatus) []string { + return []string{ + n.Node.PubKey, + n.Node.NetID, + strconv.FormatBool(n.Node.IsAlive), + strconv.FormatBool(n.ForCommittee), + strconv.FormatBool(n.ForAccess), + n.AccessAPI, + } + } + printNodes := func(label string, nodes []*model.ChainNodeStatus) { + if nodes == nil { + log.Printf("%s: N/A\n", label) + } + log.Printf("%s: %v\n", label, len(nodes)) + rows := make([][]string, 0) + for _, n := range nodes { + rows = append(rows, printNodesRowFmt(n)) + } + log.PrintTable(printNodesRowHdr, rows) + } + + log.Printf("Chain ID: %s\n", chainInfo.ChainID) + log.Printf("Active: %v\n", chainInfo.Active) - log.Printf("Chain ID: %s\n", chain.ChainID.Base58()) - log.Printf("Committee nodes: %+v\n", committee.Nodes) - log.Printf("Active: %v\n", chain.Active) + if chainInfo.Active { + log.Printf("State address: %v\n", chainInfo.StateAddress) + printNodes("Committee nodes", chainInfo.CommitteeNodes) + printNodes("Access nodes", chainInfo.AccessNodes) + printNodes("Candidate nodes", chainInfo.CandidateNodes) - if chain.Active { info, err := SCClient(governance.Contract.Hname()).CallView(governance.FuncGetChainInfo.Name, nil) log.Check(err) From 5993c653837644e1f0ef44b9c05bc51955cc9354 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Mon, 27 Dec 2021 17:03:51 -0800 Subject: [PATCH 020/120] Interim save --- .../fair_roulette_service.ts | 2 +- .../wasp_client/binary_models/IOffLedger.ts | 3 +- .../wasp_client/binary_models/off_ledger.ts | 18 ++- .../wasp_client/binary_models/on_ledger.ts | 3 - .../wasm/testwasmlib/go/testwasmlib/consts.go | 2 +- .../testwasmlib/go/testwasmlib/structs.go | 65 ++++++++ .../go/testwasmlibclient/service.go | 6 +- contracts/wasm/testwasmlib/schema.yaml | 36 ++++- contracts/wasm/testwasmlib/src/consts.rs | 2 +- contracts/wasm/testwasmlib/src/structs.rs | 73 +++++++++ .../wasm/testwasmlib/ts/testwasmlib/consts.ts | 2 +- .../testwasmlib/ts/testwasmlib/structs.ts | 73 +++++++++ .../ts/testwasmlibclient/service.ts | 2 +- .../vm/wasmlib/ts/wasmclient/arguments.ts | 69 +++++---- .../wasmlib/ts/wasmclient/crypto/ed25519.ts | 6 +- .../vm/wasmlib/ts/wasmclient/crypto/seed.ts | 3 +- packages/vm/wasmlib/ts/wasmclient/event.ts | 40 ++--- packages/vm/wasmlib/ts/wasmclient/index.ts | 2 + packages/vm/wasmlib/ts/wasmclient/results.ts | 48 +++--- packages/vm/wasmlib/ts/wasmclient/service.ts | 54 ++++--- .../vm/wasmlib/ts/wasmclient/serviceclient.ts | 144 ++++++++++++++++++ packages/vm/wasmlib/ts/wasmclient/transfer.ts | 50 ++++++ .../generator/goclienttemplates/service.go | 6 +- .../generator/tsclienttemplates/events.go | 2 +- .../generator/tsclienttemplates/service.go | 2 +- 25 files changed, 581 insertions(+), 132 deletions(-) create mode 100644 contracts/wasm/testwasmlib/go/testwasmlib/structs.go create mode 100644 contracts/wasm/testwasmlib/src/structs.rs create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlib/structs.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/serviceclient.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/transfer.ts diff --git a/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts b/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts index 61bb988f02..a50ff3b622 100644 --- a/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts +++ b/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts @@ -122,7 +122,7 @@ export class FairRouletteService { balances: [{ balance: take, color: Colors.IOTA_COLOR_BYTES }], contract: HName.HashAsNumber(this.scName), entrypoint: HName.HashAsNumber(this.scPlaceBet), - noonce: BigInt(performance.now() + performance.timeOrigin * 10000000), + nonce: BigInt(performance.now() + performance.timeOrigin * 10000000), }; betRequest = OffLedger.Sign(betRequest, keyPair); diff --git a/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/IOffLedger.ts b/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/IOffLedger.ts index cbe10ce46f..35e52b1fcb 100644 --- a/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/IOffLedger.ts +++ b/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/IOffLedger.ts @@ -12,10 +12,11 @@ export interface Balance { export interface IOffLedger { requestType?: number; + chainID: Buffer; contract: number; entrypoint: number; arguments: OffLedgerArgument[]; - noonce: bigint; + nonce: bigint; balances: Balance[]; // Public Key and Signature will get set in the Sign function, so no inital set is required diff --git a/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/off_ledger.ts b/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/off_ledger.ts index cd9e674287..f25491249d 100644 --- a/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/off_ledger.ts +++ b/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/off_ledger.ts @@ -7,6 +7,7 @@ import type { IOffLedger } from './IOffLedger'; export class OffLedger { public static ToStruct(buffer: Buffer): IOffLedger { + const chainIDSize = 32; const publicKeySize = 32; const colorLength = 32; const signatureSize = 64; @@ -14,6 +15,7 @@ export class OffLedger { const reader = new SimpleBufferCursor(buffer); const requestType = reader.readIntBE(1); + const chainID = reader.readBytes(chainIDSize); const contract = reader.readUInt32LE(); const entrypoint = reader.readUInt32LE(); const numArguments = reader.readUInt32LE(); @@ -46,11 +48,12 @@ export class OffLedger { const offLedgerStruct: IOffLedger = { requestType: requestType, + chainID: Buffer.from(chainID), contract: contract, entrypoint: entrypoint, arguments: args, publicKey: Buffer.from(publicKey), - noonce: noonce, + nonce: noonce, balances: balances, signature: Buffer.from(signature), }; @@ -61,15 +64,15 @@ export class OffLedger { public static ToBuffer(req: IOffLedger): Buffer { const buffer = new SimpleBufferCursor(Buffer.alloc(0)); - if ([0, 1].includes(req.requestType)) { - buffer.writeIntBE(req.requestType, 1); + if (req.requestType) { + buffer.writeInt8(1); } + buffer.writeBytes(req.chainID); buffer.writeUInt32LE(req.contract); buffer.writeUInt32LE(req.entrypoint); buffer.writeUInt32LE(req.arguments.length || 0); - if (req.arguments) { req.arguments.sort((lhs,rhs)=> lhs.key.localeCompare(rhs.key)); for (const arg of req.arguments) { @@ -78,6 +81,7 @@ export class OffLedger { buffer.writeUInt16LE(keyBuffer.length); buffer.writeBytes(keyBuffer); + //TODO FIXME: always a number? const valueBuffer = Buffer.alloc(8); valueBuffer.writeInt32LE(arg.value, 0); @@ -87,10 +91,9 @@ export class OffLedger { } buffer.writeBytes(req.publicKey); - buffer.writeUInt64LE(req.noonce); + buffer.writeUInt64LE(req.nonce); buffer.writeUInt32LE(req.balances.length || 0); - if (req.balances) { for (const balance of req.balances) { buffer.writeBytes(balance.color); @@ -111,9 +114,10 @@ export class OffLedger { const cleanCopyOfRequest: IOffLedger = { arguments: request.arguments, balances: request.balances, + chainID: request.chainID, contract: request.contract, entrypoint: request.entrypoint, - noonce: request.noonce, + nonce: request.nonce, publicKey: keyPair.publicKey, requestType: null, diff --git a/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/on_ledger.ts b/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/on_ledger.ts index 968bbaac3b..e991e2d2ee 100644 --- a/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/on_ledger.ts +++ b/contracts/wasm/fairroulette/frontend/src/lib/wasp_client/binary_models/on_ledger.ts @@ -11,13 +11,11 @@ export class OnLedger { const numArguments = reader.readUInt32LE(); const args = []; - for (let i = 0; i < numArguments; i++) { const sz16 = reader.readUInt16LE(); const key = reader.readBytes(sz16); const sz32 = reader.readUInt32LE(); const value = reader.readBytes(sz32); - args.push({ key: key, value: value }); } @@ -40,7 +38,6 @@ export class OnLedger { buffer.writeBytes(Buffer.alloc(1, 0)); buffer.writeUInt32LE(req.arguments.length || 0); - if (req.arguments) { req.arguments.sort((lhs,rhs)=> lhs.key.localeCompare(rhs.key)); for (const arg of req.arguments) { diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/consts.go b/contracts/wasm/testwasmlib/go/testwasmlib/consts.go index bfafdc9086..a6fc962005 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/consts.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/consts.go @@ -11,7 +11,7 @@ import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" const ( ScName = "testwasmlib" - ScDescription = "Exercise all aspects of WasmLib" + ScDescription = "Exercise several aspects of WasmLib" HScName = wasmlib.ScHname(0x89703a45) ) diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/structs.go b/contracts/wasm/testwasmlib/go/testwasmlib/structs.go new file mode 100644 index 0000000000..f57de29a9d --- /dev/null +++ b/contracts/wasm/testwasmlib/go/testwasmlib/structs.go @@ -0,0 +1,65 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package testwasmlib + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" + +type Location struct { + X int32 + Y int32 +} + +func NewLocationFromBytes(bytes []byte) *Location { + decode := wasmlib.NewBytesDecoder(bytes) + data := &Location{} + data.X = decode.Int32() + data.Y = decode.Int32() + decode.Close() + return data +} + +func (o *Location) Bytes() []byte { + return wasmlib.NewBytesEncoder(). + Int32(o.X). + Int32(o.Y). + Data() +} + +type ImmutableLocation struct { + objID int32 + keyID wasmlib.Key32 +} + +func (o ImmutableLocation) Exists() bool { + return wasmlib.Exists(o.objID, o.keyID, wasmlib.TYPE_BYTES) +} + +func (o ImmutableLocation) Value() *Location { + return NewLocationFromBytes(wasmlib.GetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES)) +} + +type MutableLocation struct { + objID int32 + keyID wasmlib.Key32 +} + +func (o MutableLocation) Delete() { + wasmlib.DelKey(o.objID, o.keyID, wasmlib.TYPE_BYTES) +} + +func (o MutableLocation) Exists() bool { + return wasmlib.Exists(o.objID, o.keyID, wasmlib.TYPE_BYTES) +} + +func (o MutableLocation) SetValue(value *Location) { + wasmlib.SetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES, value.Bytes()) +} + +func (o MutableLocation) Value() *Location { + return NewLocationFromBytes(wasmlib.GetBytes(o.objID, o.keyID, wasmlib.TYPE_BYTES)) +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go index 2c82798b87..c0157362d2 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go @@ -367,10 +367,10 @@ type TestWasmLibService struct { wasmclient.Service } -func NewTestWasmLibService(cl *wasmclient.ServiceClient, chainID string) *TestWasmLibService { +func NewTestWasmLibService(cl *wasmclient.ServiceClient, chainID string) (*TestWasmLibService, error) { s := &TestWasmLibService{} - s.Service.Init(cl, chainID, 0x89703a45, EventHandlers) - return s + err := s.Service.Init(cl, chainID, 0x89703a45, EventHandlers) + return s, err } func (s *TestWasmLibService) ArrayClear() ArrayClearFunc { diff --git a/contracts/wasm/testwasmlib/schema.yaml b/contracts/wasm/testwasmlib/schema.yaml index 1ec756a5f2..3a9d9d8086 100644 --- a/contracts/wasm/testwasmlib/schema.yaml +++ b/contracts/wasm/testwasmlib/schema.yaml @@ -1,27 +1,50 @@ name: TestWasmLib -description: Exercise all aspects of WasmLib +description: Exercise several aspects of WasmLib + +# ################################## events: +# ################################## test: name: String address: Address -structs: {} + +# ################################## +structs: +# ################################## +# Location: +# x: Int32 +# y: Int32 + +# ################################## typedefs: +# ################################## StringArray: String[] +# Longitude: map[Int32]Location + +# ################################## state: +# ################################## arrays: map[String]StringArray random: Int64 +# latLong: map[Int32]Longitude + +# ################################## funcs: +# ################################## arrayClear: params: name: String + arrayCreate: params: name: String + arraySet: params: index: Int32 name: String value: String + paramTypes: params: address: Address? @@ -43,37 +66,46 @@ funcs: uint16: Uint16? uint32: Uint32? uint64: Uint64? + random: triggerEvent: params: name: String address: Address + +# ################################## views: +# ################################## arrayLength: params: name: String results: length: Int32 + arrayValue: params: index: Int32 name: String results: value: String + blockRecord: params: blockIndex: Int32 recordIndex: Int32 results: record: Bytes + blockRecords: params: blockIndex: Int32 results: count: Int32 + iotaBalance: results: iotas: Int64 + getRandom: results: random: Int64 diff --git a/contracts/wasm/testwasmlib/src/consts.rs b/contracts/wasm/testwasmlib/src/consts.rs index 9fedc8aeb7..31eec7ad2e 100644 --- a/contracts/wasm/testwasmlib/src/consts.rs +++ b/contracts/wasm/testwasmlib/src/consts.rs @@ -10,7 +10,7 @@ use wasmlib::*; pub const SC_NAME : &str = "testwasmlib"; -pub const SC_DESCRIPTION : &str = "Exercise all aspects of WasmLib"; +pub const SC_DESCRIPTION : &str = "Exercise several aspects of WasmLib"; pub const HSC_NAME : ScHname = ScHname(0x89703a45); pub const PARAM_ADDRESS : &str = "address"; diff --git a/contracts/wasm/testwasmlib/src/structs.rs b/contracts/wasm/testwasmlib/src/structs.rs new file mode 100644 index 0000000000..9d670e8dee --- /dev/null +++ b/contracts/wasm/testwasmlib/src/structs.rs @@ -0,0 +1,73 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +#![allow(dead_code)] +#![allow(unused_imports)] + +use wasmlib::*; +use wasmlib::host::*; +use crate::typedefs::*; + +pub struct Location { + pub x : i32, + pub y : i32, +} + +impl Location { + pub fn from_bytes(bytes: &[u8]) -> Location { + let mut decode = BytesDecoder::new(bytes); + Location { + x : decode.int32(), + y : decode.int32(), + } + } + + pub fn to_bytes(&self) -> Vec { + let mut encode = BytesEncoder::new(); + encode.int32(self.x); + encode.int32(self.y); + return encode.data(); + } +} + +pub struct ImmutableLocation { + pub(crate) obj_id: i32, + pub(crate) key_id: Key32, +} + +impl ImmutableLocation { + pub fn exists(&self) -> bool { + exists(self.obj_id, self.key_id, TYPE_BYTES) + } + + pub fn value(&self) -> Location { + Location::from_bytes(&get_bytes(self.obj_id, self.key_id, TYPE_BYTES)) + } +} + +pub struct MutableLocation { + pub(crate) obj_id: i32, + pub(crate) key_id: Key32, +} + +impl MutableLocation { + pub fn delete(&self) { + del_key(self.obj_id, self.key_id, TYPE_BYTES); + } + + pub fn exists(&self) -> bool { + exists(self.obj_id, self.key_id, TYPE_BYTES) + } + + pub fn set_value(&self, value: &Location) { + set_bytes(self.obj_id, self.key_id, TYPE_BYTES, &value.to_bytes()); + } + + pub fn value(&self) -> Location { + Location::from_bytes(&get_bytes(self.obj_id, self.key_id, TYPE_BYTES)) + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts index ba2862e126..daf3f5ed6a 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts @@ -8,7 +8,7 @@ import * as wasmlib from "wasmlib"; export const ScName = "testwasmlib"; -export const ScDescription = "Exercise all aspects of WasmLib"; +export const ScDescription = "Exercise several aspects of WasmLib"; export const HScName = new wasmlib.ScHname(0x89703a45); export const ParamAddress = "address"; diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/structs.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/structs.ts new file mode 100644 index 0000000000..943ee88121 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/structs.ts @@ -0,0 +1,73 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmlib from "wasmlib"; + +export class Location { + x : i32 = 0; + y : i32 = 0; + + static fromBytes(bytes: u8[]): Location { + let decode = new wasmlib.BytesDecoder(bytes); + let data = new Location(); + data.x = decode.int32(); + data.y = decode.int32(); + decode.close(); + return data; + } + + bytes(): u8[] { + return new wasmlib.BytesEncoder(). + int32(this.x). + int32(this.y). + data(); + } +} + +export class ImmutableLocation { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + value(): Location { + return Location.fromBytes(wasmlib.getBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES)); + } +} + +export class MutableLocation { + objID: i32; + keyID: wasmlib.Key32; + + constructor(objID: i32, keyID: wasmlib.Key32) { + this.objID = objID; + this.keyID = keyID; + } + + delete(): void { + wasmlib.delKey(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + exists(): boolean { + return wasmlib.exists(this.objID, this.keyID, wasmlib.TYPE_BYTES); + } + + setValue(value: Location): void { + wasmlib.setBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES, value.bytes()); + } + + value(): Location { + return Location.fromBytes(wasmlib.getBytes(this.objID, this.keyID, wasmlib.TYPE_BYTES)); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts index f6e53308ea..b8726e9fac 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -5,7 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -import * as wasmclient from "wasmlib/wasmclient" +import * as wasmclient from "wasmclient" import * as events from "./events" const ArgAddress = "address"; diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 985e567e1f..31f73ee092 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -1,44 +1,43 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import * as wasmclient from "./index" import * as wasmlib from "../wasmlib" -import * as client from "./index" import {Base58} from "./crypto"; import {Buffer} from "./buffer"; -import {SimpleBufferCursor} from "../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; // The Arguments struct is used to gather all arguments for this smart // contract function call and encode it into this deterministic byte array export class Arguments { - args = new Map(); + private args = new Map(); - private set(key: string, val: client.Bytes): void { + private set(key: string, val: wasmclient.Bytes): void { this.args.set(key, val); } - private setBase58(key: string, val: string, typeID: client.Int32): void { + private setBase58(key: string, val: string, typeID: wasmclient.Int32): void { let bytes = Base58.decode(val); if (bytes.length != wasmlib.TYPE_SIZES[typeID]) { - client.panic("invalid byte size"); + wasmclient.panic("invalid byte size"); } this.set(key, bytes); } - indexedKey(key: string, index: client.Int32): string { + indexedKey(key: string, index: wasmclient.Int32): string { return key + "." + index.toString(); } mandatory(key: string): void { if (!this.args.has(key)) { - client.panic("missing mandatory " + key) + wasmclient.panic("missing mandatory " + key) } } - setAddress(key: string, val: client.AgentID): void { + setAddress(key: string, val: wasmclient.AgentID): void { this.setBase58(key, val, wasmlib.TYPE_ADDRESS); } - setAgentID(key: string, val: client.AgentID): void { + setAgentID(key: string, val: wasmclient.AgentID): void { this.setBase58(key, val, wasmlib.TYPE_AGENT_ID); } @@ -50,47 +49,47 @@ export class Arguments { this.set(key, bytes) } - setBytes(key: string, val: client.Bytes): void { + setBytes(key: string, val: wasmclient.Bytes): void { this.set(key, Buffer.from(val)); } - setColor(key: string, val: client.Color): void { + setColor(key: string, val: wasmclient.Color): void { this.setBase58(key, val, wasmlib.TYPE_COLOR); } - setChainID(key: string, val: client.ChainID): void { + setChainID(key: string, val: wasmclient.ChainID): void { this.setBase58(key, val, wasmlib.TYPE_CHAIN_ID); } - setHash(key: string, val: client.Hash): void { + setHash(key: string, val: wasmclient.Hash): void { this.setBase58(key, val, wasmlib.TYPE_HASH); } - setInt8(key: string, val: client.Int8): void { + setInt8(key: string, val: wasmclient.Int8): void { let bytes = Buffer.alloc(1); bytes.writeInt8(val, 0); this.set(key, bytes); } - setInt16(key: string, val: client.Int16): void { + setInt16(key: string, val: wasmclient.Int16): void { let bytes = Buffer.alloc(2); bytes.writeInt16LE(val, 0); this.set(key, bytes); } - setInt32(key: string, val: client.Int32): void { + setInt32(key: string, val: wasmclient.Int32): void { let bytes = Buffer.alloc(4); bytes.writeInt32LE(val, 0); this.set(key, bytes); } - setInt64(key: string, val: client.Int64): void { + setInt64(key: string, val: wasmclient.Int64): void { let bytes = Buffer.alloc(8); bytes.writeBigInt64LE(val, 0); this.set(key, bytes); } - setRequestID(key: string, val: client.RequestID): void { + setRequestID(key: string, val: wasmclient.RequestID): void { this.setBase58(key, val, wasmlib.TYPE_REQUEST_ID); } @@ -98,53 +97,53 @@ export class Arguments { this.set(key, Buffer.from(val)); } - setUint8(key: string, val: client.Uint8): void { + setUint8(key: string, val: wasmclient.Uint8): void { let bytes = Buffer.alloc(1); bytes.writeUInt8(val, 0); this.set(key, bytes); } - setUint16(key: string, val: client.Uint16): void { + setUint16(key: string, val: wasmclient.Uint16): void { let bytes = Buffer.alloc(2); bytes.writeUInt16LE(val, 0); this.set(key, bytes); } - setUint32(key: string, val: client.Uint32): void { + setUint32(key: string, val: wasmclient.Uint32): void { let bytes = Buffer.alloc(4); bytes.writeUInt32LE(val, 0); this.set(key, bytes); } - setUint64(key: string, val: client.Uint64): void { + setUint64(key: string, val: wasmclient.Uint64): void { let bytes = Buffer.alloc(8); bytes.writeBigUInt64LE(val, 0); this.set(key, bytes); } - // Encode returns this byte array that encodes the Arguments as follows: + // Encode returns a byte array that encodes the Transfer as follows: // Sort all keys in ascending order (very important, because this data // will be part of the data that will be signed, so the order needs to - // be 100% deterministic). Then emit this 2-byte argument count. - // Next for each argument emit this 2-byte key length, the key prepended - // with this minus sign, this 4-byte value length, and then the value bytes. - encode(): client.Bytes { + // be 100% deterministic). Then emit the 4-byte argument count. + // Next for each argument emit the 2-byte key length, the key prepended + // with this minus sign, the 4-byte value length, and then the value bytes. + encode(): wasmclient.Bytes { let keys = new Array(); for (const key of this.args.keys()) { keys.push(key); } keys.sort((lhs, rhs) => lhs.localeCompare(rhs)); - const buf = new SimpleBufferCursor(Buffer.alloc(0)); - buf.writeUInt32LE(keys.length); + let buf = Buffer.alloc(0); + buf.writeUInt32LE(keys.length, 0); for (const key of keys) { let keyBuf = Buffer.from("-" + key); - buf.writeUInt16LE(keyBuf.length); - buf.writeBytes(keyBuf); + buf.writeUInt16LE(keyBuf.length, buf.length); + buf = Buffer.concat([buf, keyBuf]); let valBuf = this.args.get(key); - buf.writeUInt32LE(valBuf.length); - buf.writeBytes(valBuf); + buf.writeUInt32LE(valBuf.length, buf.length); + buf = Buffer.concat([buf, valBuf]); } - return buf.buffer; + return buf; } } diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts index c01b033571..2991e2311f 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts @@ -1,6 +1,10 @@ import nacl from 'tweetnacl'; import { Buffer } from '../buffer'; -import type { IKeyPair } from '../models'; + +export interface IKeyPair { + publicKey: Buffer; + secretKey: Buffer; +} /** * Class to help with ED25519 Signature scheme. diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts index cc805894a5..dafbabbfb4 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts @@ -1,8 +1,7 @@ import { Base58 } from './base58'; import { blake2b } from 'blakejs'; import { Buffer } from '../buffer'; -import { ED25519 } from './ed25519'; -import type { IKeyPair } from '../models'; +import { ED25519, IKeyPair } from './ed25519'; export class Seed { /** diff --git a/packages/vm/wasmlib/ts/wasmclient/event.ts b/packages/vm/wasmlib/ts/wasmclient/event.ts index 747cd96148..56d19d832a 100644 --- a/packages/vm/wasmlib/ts/wasmclient/event.ts +++ b/packages/vm/wasmlib/ts/wasmclient/event.ts @@ -1,13 +1,13 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import * as client from "./index" +import * as wasmclient from "./index" import {Base58} from "./crypto"; export class Event { - private index: client.Int32; + private index: wasmclient.Int32; private message: string[]; - public readonly timestamp: client.Int32; + public readonly timestamp: wasmclient.Int32; constructor(message: string[]) { this.message = message; @@ -19,55 +19,55 @@ export class Event { return this.message[this.index++]; } - nextAddress(): client.Address { + nextAddress(): wasmclient.Address { return this.next(); } - nextAgentID(): client.AgentID { + nextAgentID(): wasmclient.AgentID { return this.next(); } - nextBool(): client.Bool { + nextBool(): wasmclient.Bool { return this.next() != "0"; } - nextBytes(): client.Bytes { + nextBytes(): wasmclient.Bytes { return Base58.decode(this.next()); } - nextChainID(): client.ChainID { + nextChainID(): wasmclient.ChainID { return this.next(); } - nextColor(): client.Color { + nextColor(): wasmclient.Color { return this.next(); } - nextHash(): client.Hash { + nextHash(): wasmclient.Hash { return this.next(); } - nextHname(): client.Hname { + nextHname(): wasmclient.Hname { return Number(this.next()); } - nextInt8(): client.Int8 { + nextInt8(): wasmclient.Int8 { return Number(this.next()); } - nextInt16(): client.Int16 { + nextInt16(): wasmclient.Int16 { return Number(this.next()); } - nextInt32(): client.Int32 { + nextInt32(): wasmclient.Int32 { return Number(this.next()); } - nextInt64(): client.Int64 { + nextInt64(): wasmclient.Int64 { return BigInt(this.next()); } - nextRequestID(): client.RequestID { + nextRequestID(): wasmclient.RequestID { return this.next(); } @@ -75,19 +75,19 @@ export class Event { return this.next(); } - nextUint8(): client.Uint8 { + nextUint8(): wasmclient.Uint8 { return Number(this.next()); } - nextUint16(): client.Uint16 { + nextUint16(): wasmclient.Uint16 { return Number(this.next()); } - nextUint32(): client.Uint32 { + nextUint32(): wasmclient.Uint32 { return Number(this.next()); } - nextUint64(): client.Uint64 { + nextUint64(): wasmclient.Uint64 { return BigInt(this.next()); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/index.ts b/packages/vm/wasmlib/ts/wasmclient/index.ts index ca4768dd66..a4fc7b16d8 100644 --- a/packages/vm/wasmlib/ts/wasmclient/index.ts +++ b/packages/vm/wasmlib/ts/wasmclient/index.ts @@ -5,4 +5,6 @@ export * from "./arguments"; export * from "./event"; export * from "./results"; export * from "./service"; +export * from "./serviceclient"; +export * from "./transfer"; export * from "./types"; diff --git a/packages/vm/wasmlib/ts/wasmclient/results.ts b/packages/vm/wasmlib/ts/wasmclient/results.ts index f6dd334a2b..7377540430 100644 --- a/packages/vm/wasmlib/ts/wasmclient/results.ts +++ b/packages/vm/wasmlib/ts/wasmclient/results.ts @@ -1,23 +1,23 @@ // The Results struct is used to gather all arguments for a smart // contract function call and encode it into a deterministic byte array +import * as wasmclient from "./index"; import * as wasmlib from "../wasmlib"; -import * as client from "./index"; import {Base58} from "./crypto"; import {Buffer} from "./buffer"; export class Results { - res = new Map(); + res = new Map(); - exists(key: string): client.Bool { + exists(key: string): wasmclient.Bool { return this.res.has(key); } - private get(key: string, typeID: client.Int32): client.Bytes { + private get(key: string, typeID: wasmclient.Int32): wasmclient.Bytes { let size = wasmlib.TYPE_SIZES[typeID]; let bytes = this.res.get(key); if (bytes !== undefined) { if (size != 0 && bytes.length != size) { - client.panic("invalid type size"); + wasmclient.panic("invalid type size"); } return bytes; } @@ -25,79 +25,79 @@ export class Results { return Buffer.alloc(size); } - private getBase58(key: string, typeID: client.Int32): string { + private getBase58(key: string, typeID: wasmclient.Int32): string { return Base58.encode(this.get(key, typeID)); } - getAddress(key: string): client.Address { + getAddress(key: string): wasmclient.Address { return this.getBase58(key, wasmlib.TYPE_ADDRESS); } - getAgentID(key: string): client.AgentID { + getAgentID(key: string): wasmclient.AgentID { return this.getBase58(key, wasmlib.TYPE_AGENT_ID); } - getBytes(key: string): client.Bytes { + getBytes(key: string): wasmclient.Bytes { return this.get(key, wasmlib.TYPE_BYTES) } - getBool(key: string): client.Bool { + getBool(key: string): wasmclient.Bool { return this.get(key, wasmlib.TYPE_BOOL)[0] != 0; } - getChainID(key: string): client.ChainID { + getChainID(key: string): wasmclient.ChainID { return this.getBase58(key, wasmlib.TYPE_CHAIN_ID); } - getColor(key: string): client.Color { + getColor(key: string): wasmclient.Color { return this.getBase58(key, wasmlib.TYPE_COLOR); } - getHash(key: string): client.Hash { + getHash(key: string): wasmclient.Hash { return this.getBase58(key, wasmlib.TYPE_HASH); } - getHname(key: string): client.Hname { + getHname(key: string): wasmclient.Hname { return this.get(key, wasmlib.TYPE_HNAME).readUInt32LE(0); } - getInt8(key: string): client.Int8 { + getInt8(key: string): wasmclient.Int8 { return this.get(key, wasmlib.TYPE_INT8).readInt8(0); } - getInt16(key: string): client.Int16 { + getInt16(key: string): wasmclient.Int16 { return this.get(key, wasmlib.TYPE_INT16).readInt16LE(0); } - getInt32(key: string): client.Int32 { + getInt32(key: string): wasmclient.Int32 { return this.get(key, wasmlib.TYPE_INT32).readInt32LE(0); } - getInt64(key: string): client.Int64 { + getInt64(key: string): wasmclient.Int64 { return this.get(key, wasmlib.TYPE_INT64).readBigInt64LE(0); } - getRequestID(key: string): client.RequestID { + getRequestID(key: string): wasmclient.RequestID { return this.getBase58(key, wasmlib.TYPE_REQUEST_ID); } - getString(key: string): client.String { + getString(key: string): wasmclient.String { return this.get(key, wasmlib.TYPE_STRING).toString(); } - getUint8(key: string): client.Uint8 { + getUint8(key: string): wasmclient.Uint8 { return this.get(key, wasmlib.TYPE_INT8).readUInt8(0); } - getUint16(key: string): client.Uint16 { + getUint16(key: string): wasmclient.Uint16 { return this.get(key, wasmlib.TYPE_INT16).readUInt16LE(0); } - getUint32(key: string): client.Uint32 { + getUint32(key: string): wasmclient.Uint32 { return this.get(key, wasmlib.TYPE_INT32).readUInt32LE(0); } - getUint64(key: string): client.Uint64 { + getUint64(key: string): wasmclient.Uint64 { return this.get(key, wasmlib.TYPE_INT64).readBigUInt64LE(0); } diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 834e3a41c4..f03748ea47 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -1,12 +1,11 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +import * as wasmclient from "./index" import * as wasp from "../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; -import {Colors, IOffLedger, OffLedger} from "../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; import config from './config.dev'; -import * as client from "./index" - -export type ServiceClient = wasp.BasicClient; +import {Base58, ED25519, IKeyPair} from "./crypto"; +import {Buffer} from "./buffer"; export type EventHandlers = { [key: string]: (message: string[]) => void }; @@ -19,22 +18,23 @@ export class FuncObject { } export class ViewResults { - res: client.Results; + res: wasmclient.Results; - constructor(res: client.Results) { + constructor(res: wasmclient.Results) { this.res = res; } } export class Service { - private client: ServiceClient; + private client: wasmclient.ServiceClient; private walletService: wasp.WalletService; private webSocket: WebSocket; private eventHandlers: EventHandlers; + private keyPair: IKeyPair; public chainId: string; - public scHname: client.Hname; + public scHname: wasmclient.Hname; - constructor(client: ServiceClient, chainId: string, scHname: client.Hname, eventHandlers: EventHandlers) { + constructor(client: ServiceClient, chainId: string, scHname: wasmclient.Hname, eventHandlers: EventHandlers) { this.client = client; this.chainId = chainId; this.scHname = scHname; @@ -66,7 +66,7 @@ export class Service { } // calls a view - public async callView(viewName: string, args: client.Arguments): Promise { + public async callView(viewName: string, args: wasmclient.Arguments): Promise { const response = await this.client.callView( this.chainId, this.scHname.toString(16), @@ -77,8 +77,8 @@ export class Service { const resultMap: ParameterResult = {}; if (response.Items) { for (let item of response.Items) { - const key = wasp.Buffer.from(item.Key, "base64").toString(); - const value = wasp.Buffer.from(item.Value, "base64"); + const key = Buffer.from(item.Key, "base64").toString(); + const value = Buffer.from(item.Value, "base64"); resultMap[key] = value; } } @@ -86,19 +86,25 @@ export class Service { } // posts off-tangle request - public async postRequest(funcName: string, args: client.Arguments): Promise { - let request: IOffLedger = { - requestType: 1, - noonce: BigInt(performance.now() + performance.timeOrigin * 10000000), - contract: this.scHname, - entrypoint: hFunc, - arguments: [{key: '-number', value: betNumber}], - balances: [{balance: take, color: Colors.IOTA_COLOR_BYTES}], - }; - - request = OffLedger.Sign(request, keyPair); + public async postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer?: wasmclient.Transfer): Promise { + if (!transfer) { + transfer = new wasmclient.Transfer(); + } + + // get request essence ready for signing + let essence = Base58.decode(this.chainId); + essence.writeUInt32LE(this.scHname, essence.length); + essence.writeUInt32LE(hFuncName, essence.length); + essence = Buffer.concat([essence, args.encode(), this.keyPair.publicKey]); + essence.writeBigUInt64LE(BigInt(performance.now()), essence.length); + essence = Buffer.concat([essence, transfer.encode()]); + + let buf = Buffer.alloc(0); + const requestTypeOffledger = 1; + buf.writeUInt8(requestTypeOffledger, 0); + buf = Buffer.concat([buf, essence, ED25519.privateSign(this.keyPair, essence)]); await this.client.sendOffLedgerRequest(this.chainId, request); - await this.client.sendExecutionRequest(this.chainId, OffLedger.GetRequestId(request)); + await this.client.sendExecutionRequest(this.chainId, wasp.OffLedger.GetRequestId(request)); } } \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts new file mode 100644 index 0000000000..f389e17199 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts @@ -0,0 +1,144 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmclient from "./index" +import {Buffer} from "./buffer"; + +export class ServiceClient { + private configuration: BasicClientConfiguration; + + constructor(configuration: BasicClientConfiguration) { + this.configuration = configuration; + } + + public async getAllowedManaPledge(): Promise { + return this.sendRequest( + this.configuration.GoShimmerAPIUrl, + 'get', + 'mana/allowedManaPledge', + ); + } + + public async sendFaucetRequest(faucetRequest: IFaucetRequest): Promise { + const response = await this.sendRequest( + this.configuration.GoShimmerAPIUrl, + 'post', + 'faucet', + faucetRequest, + ); + + return response; + } + + public async sendOffLedgerRequest(chainId: string, offLedgerRequest: Buffer): Promise { + const request = { Request: offLedgerRequest.toString('base64') }; + + await this.sendRequestExt( + this.configuration.WaspAPIUrl, + 'post', + `request/${chainId}`, + request, + ); + } + + public async sendExecutionRequest(chainId: string, offLedgerRequestId: string): Promise { + await this.sendRequestExt( + this.configuration.WaspAPIUrl, + 'get', + `chain/${chainId}/request/${offLedgerRequestId}/wait`, + ); + } + + public async callView(chainId: string, contractHName: string, entryPoint: string): Promise { + const url = `chain/${chainId}/contract/${contractHName}/callview/${entryPoint}`; + + const result = await this.sendRequestExt(this.configuration.WaspAPIUrl, 'get', url); + + return result.body; + } + + public async getAddressUnspentOutputs(address: string): Promise { + return this.sendRequest( + this.configuration.GoShimmerAPIUrl, + 'get', + `ledgerstate/addresses/${address}/unspentOutputs`, + ); + } + + public async unspentOutputs(request: IUnspentOutputsRequest): Promise { + return this.sendRequest( + this.configuration.GoShimmerAPIUrl, + 'post', + 'ledgerstate/addresses/unspentOutputs', + request, + ); + } + + public async sendTransaction(request: ISendTransactionRequest): Promise { + return this.sendRequest( + this.configuration.GoShimmerAPIUrl, + 'post', + 'ledgerstate/transactions', + request, + ); + } + + private async sendRequest( + url: string, + verb: 'put' | 'post' | 'get' | 'delete', + path: string, + request?: T | undefined, + ): Promise { + const response = await this.sendRequestExt(url, verb, path, request); + + return response.body; + } + + private async sendRequestExt( + url: string, + verb: 'put' | 'post' | 'get' | 'delete', + path: string, + request?: T | undefined, + ): Promise> { + let response: U; + let fetchResponse: Response; + + try { + const headers: { [id: string]: string } = { + 'Content-Type': 'application/json', + }; + + if (verb == 'get' || verb == 'delete') { + fetchResponse = await fetch(`${url}/${path}`, { + method: verb, + headers, + }); + } else if (verb == 'post' || verb == 'put') { + fetchResponse = await fetch(`${url}/${path}`, { + method: verb, + headers, + body: JSON.stringify(request), + }); + } + + if (!fetchResponse) { + throw new Error('No data was returned from the API'); + } + + try { + response = await fetchResponse.json(); + } catch (err) { + if (!fetchResponse.ok) { + const text = await fetchResponse.text(); + throw new Error(err.message + ' --- ' + text); + } + } + } catch (err) { + throw new Error( + `The application is not able to complete the request, due to the following error:\n\n${err.message}`, + ); + } + + return { body: response, response: fetchResponse }; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/transfer.ts b/packages/vm/wasmlib/ts/wasmclient/transfer.ts new file mode 100644 index 0000000000..01d14dc48b --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/transfer.ts @@ -0,0 +1,50 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmclient from "./index" +import {Base58} from "./crypto"; +import {Buffer} from "./buffer"; + +export class Transfer { + private xfer = new Map(); + + static iotas(amount: wasmclient.Uint64): Transfer { + const iotaColorBase58 = "11111111111111111111111111111111"; + return Transfer.tokens(iotaColorBase58, amount); + } + + static tokens(color: string, amount: wasmclient.Uint64): Transfer { + let transfers = new Transfer(); + transfers.set(color, amount); + return transfers; + } + + set(color: string, amount: wasmclient.Uint64) { + this.xfer.set(Base58.decode(color), amount); + } + + // Encode returns a byte array that encodes the Transfer as follows: + // Sort all nonzero transfers in ascending color order (very important, + // because this data will be part of the data that will be signed, + // so the order needs to be 100% deterministic). Then emit the 4-byte + // transfer count. Next for each color emit the 32-byte color value, + // and then the 8-byte amount. + encode(): wasmclient.Bytes { + let keys = new Array(); + for (const [key,val] of this.xfer) { + // filter out zero transfers + if (val != BigInt(0)) { + keys.push(key); + } + } + keys.sort((lhs, rhs) => lhs.compare(rhs)); + + let buf = Buffer.alloc(0); + buf.writeUInt32LE(keys.length, 0); + for (const key of keys) { + buf = Buffer.concat([buf, key]); + buf.writeBigUInt64LE(this.xfer.get(key), buf.length); + } + return buf; + } +} \ No newline at end of file diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index 37f9e7000a..7f6e9f9dc9 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -18,10 +18,10 @@ type $PkgName$+Service struct { wasmclient.Service } -func New$PkgName$+Service(cl *wasmclient.ServiceClient, chainID string) *$PkgName$+Service { +func New$PkgName$+Service(cl *wasmclient.ServiceClient, chainID string) (*$PkgName$+Service, error) { s := &$PkgName$+Service{} - s.Service.Init(cl, chainID, 0x$hscName, EventHandlers) - return s + err := s.Service.Init(cl, chainID, 0x$hscName, EventHandlers) + return s, err } $#each func serviceFunction `, diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go index eca05dc74d..c481b11540 100644 --- a/tools/schema/generator/tsclienttemplates/events.go +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -3,7 +3,7 @@ package tsclienttemplates var eventsTs = map[string]string{ // ******************************* "events.ts": ` -import * as wasmclient from "wasmlib/wasmclient" +import * as wasmclient from "wasmclient" import * as app from "./$package" export const eventHandlers: wasmclient.EventHandlers = { diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index b8ace4357c..947bad07c1 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -3,7 +3,7 @@ package tsclienttemplates var serviceTs = map[string]string{ // ******************************* "service.ts": ` -import * as wasmclient from "wasmlib/wasmclient" +import * as wasmclient from "wasmclient" import * as events from "./events" $#each params constArg From 0d0325c8b3572cb01e9ea6b74d82eb862eefc47b Mon Sep 17 00:00:00 2001 From: Critical <53269239+Critical94@users.noreply.github.com> Date: Tue, 28 Dec 2021 02:24:55 +0100 Subject: [PATCH 021/120] Fix broken link --- documentation/docs/guide/evm/examples/ERC20.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/docs/guide/evm/examples/ERC20.md b/documentation/docs/guide/evm/examples/ERC20.md index 57084d5630..2b06394e3c 100644 --- a/documentation/docs/guide/evm/examples/ERC20.md +++ b/documentation/docs/guide/evm/examples/ERC20.md @@ -20,7 +20,7 @@ With the ERC20 standart you can create your own tokens and transfer them in the You can use the [Remix IDE](https://remix.ethereum.org/) to deploy any regular Solidity Smart Contract. Set the environment to `Injected Web3`, and connect Remix with your MetaMask wallet. -Read this [how to connect your Metamask with the public Testnet.](/wasp/guide/chains_and_nodes/testnet#interact-with-evm). +Read this [how to connect your Metamask with the public Testnet.](/smart-contracts/guide/chains_and_nodes/testnet#interact-with-evm). ## 1. Create a Smart Contract @@ -74,4 +74,4 @@ Now you should see your token in MetaMask - send them to your friends without an [![Copy contract address](./images/erc20-balance.png)](./images/erc20-balance.png) -You also can ask in the [Discord Chat Server](https://discord.iota.org) to send them around and discover what the community is building on IOTA Smart Contracts. \ No newline at end of file +You also can ask in the [Discord Chat Server](https://discord.iota.org) to send them around and discover what the community is building on IOTA Smart Contracts. From e24b5bb4a1fa6575634024d876224dd70291124b Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Tue, 28 Dec 2021 19:07:49 +0200 Subject: [PATCH 022/120] Access nodes are added by the cluster tool. --- client/node_ownership.go | 26 ++++++ packages/parameters/parameters.go | 10 ++- packages/vm/core/governance/accessnodes.go | 53 +++++++++-- .../governanceimpl/accessnodesImpl.go | 5 ++ packages/vm/core/testcore/governance_test.go | 4 +- packages/webapi/admapi/endpoints.go | 1 + packages/webapi/admapi/node_owner.go | 88 +++++++++++++++++++ .../webapi/model/node_owner_certificate.go | 13 +++ packages/webapi/routes/routes.go | 4 + plugins/config/plugin.go | 3 + tools/cluster/cluster.go | 87 +++++++++++++++--- tools/cluster/config.go | 14 +-- tools/cluster/templates/waspconfig.go | 9 +- 13 files changed, 277 insertions(+), 40 deletions(-) create mode 100644 client/node_ownership.go create mode 100644 packages/webapi/admapi/node_owner.go create mode 100644 packages/webapi/model/node_owner_certificate.go diff --git a/client/node_ownership.go b/client/node_ownership.go new file mode 100644 index 0000000000..6cde662c32 --- /dev/null +++ b/client/node_ownership.go @@ -0,0 +1,26 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package client + +import ( + "net/http" + + "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/wasp/packages/vm/core/governance" + "github.com/iotaledger/wasp/packages/webapi/model" + "github.com/iotaledger/wasp/packages/webapi/routes" +) + +func (c *WaspClient) NodeOwnershipCertificate(nodePubKey ed25519.PublicKey, ownerAddress ledgerstate.Address) (governance.NodeOwnershipCertificate, error) { + req := model.NodeOwnerCertificateRequest{ + NodePubKey: model.NewBytes(nodePubKey.Bytes()), + OwnerAddress: model.NewAddress(ownerAddress), + } + res := model.NodeOwnerCertificateResponse{} + if err := c.do(http.MethodPost, routes.AdmNodeOwnerCertificate(), req, &res); err != nil { + return nil, err + } + return governance.NewNodeOwnershipCertificateFromBytes(res.Certificate.Bytes()), nil +} diff --git a/packages/parameters/parameters.go b/packages/parameters/parameters.go index 458d9b063d..2b9b42255e 100644 --- a/packages/parameters/parameters.go +++ b/packages/parameters/parameters.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package parameters import ( @@ -12,6 +15,8 @@ import ( var all *configuration.Configuration const ( + NodeOwnerAddresses = "node.ownerAddresses" + LoggerLevel = "logger.level" LoggerDisableCaller = "logger.disableCaller" LoggerDisableStacktrace = "logger.disableStacktrace" @@ -34,7 +39,6 @@ const ( PeeringMyNetID = "peering.netid" PeeringPort = "peering.port" - PeeringNeighbors = "peering.neighbors" PullMissingRequestsFromCommittee = "peering.pullMissingRequests" NanomsgPublisherPort = "nanomsg.port" @@ -57,6 +61,8 @@ func Init() *configuration.Configuration { // set the default logger config all = configuration.New() + flag.StringSlice(NodeOwnerAddresses, []string{}, "A list of node owner addresses.") + flag.String(LoggerLevel, "info", "log level") flag.Bool(LoggerDisableCaller, false, "disable caller info in log") flag.Bool(LoggerDisableStacktrace, false, "disable stack trace in log") @@ -79,7 +85,7 @@ func Init() *configuration.Configuration { flag.Int(PeeringPort, 4000, "port for Wasp committee connection/peering") flag.String(PeeringMyNetID, "127.0.0.1:4000", "node host address as it is recognized by other peers") - flag.StringSlice(PeeringNeighbors, []string{}, "list of neighbors: known peer netIDs") + flag.Bool(PullMissingRequestsFromCommittee, true, "whether or not to pull missing requests from other committee members") flag.Int(NanomsgPublisherPort, 5550, "the port for nanomsg even publisher") diff --git a/packages/vm/core/governance/accessnodes.go b/packages/vm/core/governance/accessnodes.go index 15b81b770c..3431a532f0 100644 --- a/packages/vm/core/governance/accessnodes.go +++ b/packages/vm/core/governance/accessnodes.go @@ -17,6 +17,38 @@ import ( "golang.org/x/xerrors" ) +// NodeOwnershipCertificate is a proof that a specified address is an owner of the specified node. +// It is implemented as a signature over the node pub key concatenated with the owner address. +type NodeOwnershipCertificate []byte + +func NewNodeOwnershipCertificate(nodeKeyPair *ed25519.KeyPair, ownerAddress ledgerstate.Address) NodeOwnershipCertificate { + certData := bytes.Buffer{} + certData.Write(nodeKeyPair.PublicKey.Bytes()) + certData.Write(ownerAddress.Bytes()) + return nodeKeyPair.PrivateKey.Sign(certData.Bytes()).Bytes() +} + +func NewNodeOwnershipCertificateFromBytes(data []byte) NodeOwnershipCertificate { + return NodeOwnershipCertificate(data) +} + +func (c NodeOwnershipCertificate) Verify(nodePubKey ed25519.PublicKey, ownerAddress ledgerstate.Address) bool { + certData := bytes.Buffer{} + certData.Write(nodePubKey.Bytes()) + certData.Write(ownerAddress.Bytes()) + signature, _, err := ed25519.SignatureFromBytes(c.Bytes()) + if err != nil { + return false + } + return nodePubKey.VerifySignature(certData.Bytes(), signature) +} + +func (c NodeOwnershipCertificate) Bytes() []byte { + return c +} + +// AccessNodeInfo conveys all the information that is maintained +// on the governance SC about a specific node. type AccessNodeInfo struct { NodePubKey []byte // Public Key of the node. Stored as a key in the SC State and Params. ValidatorAddr []byte // Address of the validator owning the node. Not sent via parameters. @@ -121,19 +153,22 @@ func (a *AccessNodeInfo) ToRevokeAccessNodeParams() dict.Dict { return d } -func (a *AccessNodeInfo) AddCertificate(nodePrivKey ed25519.PrivateKey, ownerAddress ledgerstate.Address) *AccessNodeInfo { - certData := bytes.Buffer{} - certData.Write(a.NodePubKey) - certData.Write(ownerAddress.Bytes()) - a.Certificate = nodePrivKey.Sign(certData.Bytes()).Bytes() +func (a *AccessNodeInfo) AddCertificate(nodeKeyPair *ed25519.KeyPair, ownerAddress ledgerstate.Address) *AccessNodeInfo { + a.Certificate = NewNodeOwnershipCertificate(nodeKeyPair, ownerAddress).Bytes() return a } func (a *AccessNodeInfo) ValidateCertificate(ctx iscp.Sandbox) bool { - signedData := bytes.Buffer{} - signedData.Write(a.NodePubKey) - signedData.Write(a.ValidatorAddr) - return ctx.Utils().ED25519().ValidSignature(signedData.Bytes(), a.NodePubKey, a.Certificate) + nodePubKey, _, err := ed25519.PublicKeyFromBytes(a.NodePubKey) + if err != nil { + return false + } + validatorAddr, _, err := ledgerstate.AddressFromBytes(a.ValidatorAddr) + if err != nil { + return false + } + cert := NewNodeOwnershipCertificateFromBytes(a.Certificate) + return cert.Verify(nodePubKey, validatorAddr) } // diff --git a/packages/vm/core/governance/governanceimpl/accessnodesImpl.go b/packages/vm/core/governance/governanceimpl/accessnodesImpl.go index b171d60c81..4f4c984519 100644 --- a/packages/vm/core/governance/governanceimpl/accessnodesImpl.go +++ b/packages/vm/core/governance/governanceimpl/accessnodesImpl.go @@ -13,6 +13,8 @@ package governanceimpl import ( + "encoding/base64" + "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/iscp/assert" "github.com/iotaledger/wasp/packages/kv/collections" @@ -54,13 +56,16 @@ func addCandidateNodeFuncHandler(ctx iscp.Sandbox) (dict.Dict, error) { ani := governance.NewAccessNodeInfoFromAddCandidateNodeParams(ctx) a.Require(ani.ValidateCertificate(ctx), "certificate invalid") + pubKeyStr := base64.StdEncoding.EncodeToString(ani.NodePubKey) accessNodeCandidates := collections.NewMap(ctx.State(), governance.VarAccessNodeCandidates) accessNodeCandidates.MustSetAt(ani.NodePubKey, ani.Bytes()) + ctx.Log().Infof("Governance::AddCandidateNode: accessNodeCandidate added, pubKey=%s", pubKeyStr) if ctx.ChainOwnerID().Address().Equals(ctx.Request().SenderAddress()) { accessNodes := collections.NewMap(ctx.State(), governance.VarAccessNodes) accessNodes.MustSetAt(ani.NodePubKey, make([]byte, 0)) + ctx.Log().Infof("Governance::AddCandidateNode: accessNode added, pubKey=%s", pubKeyStr) } return nil, nil diff --git a/packages/vm/core/testcore/governance_test.go b/packages/vm/core/testcore/governance_test.go index 99e5c5330e..2e0b87c082 100644 --- a/packages/vm/core/testcore/governance_test.go +++ b/packages/vm/core/testcore/governance_test.go @@ -141,7 +141,7 @@ func TestAccessNodes(t *testing.T) { NodePubKey: node1KP.PublicKey.Bytes(), ForCommittee: false, AccessAPI: "http://my-api/url", - }).AddCertificate(node1KP.PrivateKey, node1OwnerAddr).ToAddCandidateNodeParams(), + }).AddCertificate(node1KP, node1OwnerAddr).ToAddCandidateNodeParams(), ).WithIotas(1), node1OwnerKP, // Sender should match data used to create the Cert field value. ) @@ -189,7 +189,7 @@ func TestAccessNodes(t *testing.T) { governance.FuncRevokeAccessNode.Name, (&governance.AccessNodeInfo{ NodePubKey: node1KP.PublicKey.Bytes(), - }).AddCertificate(node1KP.PrivateKey, node1OwnerAddr).ToAddCandidateNodeParams(), + }).AddCertificate(node1KP, node1OwnerAddr).ToAddCandidateNodeParams(), ).WithIotas(1), node1OwnerKP, // Sender should match data used to create the Cert field value. ) diff --git a/packages/webapi/admapi/endpoints.go b/packages/webapi/admapi/endpoints.go index 6042b8d401..08c9e7dbf0 100644 --- a/packages/webapi/admapi/endpoints.go +++ b/packages/webapi/admapi/endpoints.go @@ -39,6 +39,7 @@ func AddEndpoints( adm.EchoGroup().Use(protected(adminWhitelist)) addShutdownEndpoint(adm, shutdown) + addNodeOwnerEndpoints(adm, registryProvider) addChainRecordEndpoints(adm, registryProvider) addChainEndpoints(adm, registryProvider, chainsProvider, network, metrics) addDKSharesEndpoints(adm, registryProvider, nodeProvider) diff --git a/packages/webapi/admapi/node_owner.go b/packages/webapi/admapi/node_owner.go new file mode 100644 index 0000000000..3e055338fc --- /dev/null +++ b/packages/webapi/admapi/node_owner.go @@ -0,0 +1,88 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package admapi + +import ( + "bytes" + "net/http" + + "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/wasp/packages/parameters" + "github.com/iotaledger/wasp/packages/registry" + "github.com/iotaledger/wasp/packages/vm/core/governance" + "github.com/iotaledger/wasp/packages/webapi/httperrors" + "github.com/iotaledger/wasp/packages/webapi/model" + "github.com/iotaledger/wasp/packages/webapi/routes" + "github.com/labstack/echo/v4" + "github.com/pangpanglabs/echoswagger/v2" +) + +func addNodeOwnerEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Provider) { + addCtx := func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + c.Set("reg", registryProvider) + return next(c) + } + } + reqExample := model.NodeOwnerCertificateRequest{ + NodePubKey: model.NewBytes([]byte{0, 1, 17}), + OwnerAddress: model.Address("any_address"), + } + resExample := model.NodeOwnerCertificateResponse{ + Certificate: model.NewBytes([]byte{0, 1, 17, 19}), + } + adm.POST(routes.AdmNodeOwnerCertificate(), handleAdmNodeOwnerCertificate, addCtx). + AddParamBody(reqExample, "Request", "Certificate request", true). + AddResponse(http.StatusOK, "Generated certificate.", resExample, nil). + SetSummary("Provides a certificate, if the node recognizes the owner.") +} + +func handleAdmNodeOwnerCertificate(c echo.Context) error { + registryProvider := c.Get("reg").(registry.Provider) + + var req model.NodeOwnerCertificateRequest + if err := c.Bind(&req); err != nil { + return httperrors.BadRequest("Invalid request body") + } + reqOwnerAddress := req.OwnerAddress.Address() + reqNodePubKeyBytes := req.NodePubKey.Bytes() + + nodeIdentity, err := registryProvider().GetNodeIdentity() + if err != nil { + return err + } + + // + // Check, if supplied node PubKey matches. + if !bytes.Equal(nodeIdentity.PublicKey.Bytes(), reqNodePubKeyBytes) { + return &httperrors.HTTPError{Code: 400, Message: "Wrong NodePubKey"} + } + + // + // Check, if owner is presented in the configuration. + nodeOwnerAddresses := parameters.GetStringSlice(parameters.NodeOwnerAddresses) + ownerAuthorized := false + for _, nodeOwnerAddress := range nodeOwnerAddresses { + nodeOwnerAddress, err := ledgerstate.AddressFromBase58EncodedString(nodeOwnerAddress) + if err != nil { + continue + } + if bytes.Equal(reqOwnerAddress.Bytes(), nodeOwnerAddress.Bytes()) { + ownerAuthorized = true + break + } + } + if !ownerAuthorized { + return &httperrors.HTTPError{Code: 403, Message: "unauthorized"} + } + + // + // Create the certificate. It consists of signature only. The data is not included. + cert := governance.NewNodeOwnershipCertificate(nodeIdentity, reqOwnerAddress) + resp := model.NodeOwnerCertificateResponse{ + Certificate: model.NewBytes(cert.Bytes()), + } + + return c.JSON(http.StatusOK, resp) +} diff --git a/packages/webapi/model/node_owner_certificate.go b/packages/webapi/model/node_owner_certificate.go new file mode 100644 index 0000000000..c6f3fb071b --- /dev/null +++ b/packages/webapi/model/node_owner_certificate.go @@ -0,0 +1,13 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package model + +type NodeOwnerCertificateRequest struct { + NodePubKey Bytes `swagger:"desc(Node pub key. (base64))"` + OwnerAddress Address `swagger:"desc(Node owner address. (base64))"` +} + +type NodeOwnerCertificateResponse struct { + Certificate Bytes `swagger:"desc(Certificate stating the ownership. (base64))"` +} diff --git a/packages/webapi/routes/routes.go b/packages/webapi/routes/routes.go index 67aeb9e673..f6ba9d4a31 100644 --- a/packages/webapi/routes/routes.go +++ b/packages/webapi/routes/routes.go @@ -87,6 +87,10 @@ func PeeringTrustedDelete(pubKey string) string { return PeeringTrustedGet(pubKey) } +func AdmNodeOwnerCertificate() string { + return "/adm/node/owner/certificate" +} + func Shutdown() string { return "/adm/shutdown" } diff --git a/plugins/config/plugin.go b/plugins/config/plugin.go index 04fc726101..87c44208ab 100644 --- a/plugins/config/plugin.go +++ b/plugins/config/plugin.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package config import ( diff --git a/tools/cluster/cluster.go b/tools/cluster/cluster.go index 7c1925acfe..fd8a56290a 100644 --- a/tools/cluster/cluster.go +++ b/tools/cluster/cluster.go @@ -20,12 +20,14 @@ import ( "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/client" + "github.com/iotaledger/wasp/client/chainclient" "github.com/iotaledger/wasp/client/goshimmer" "github.com/iotaledger/wasp/client/multiclient" "github.com/iotaledger/wasp/packages/apilib" "github.com/iotaledger/wasp/packages/iscp/colored" "github.com/iotaledger/wasp/packages/testutil/testkey" "github.com/iotaledger/wasp/packages/util" + "github.com/iotaledger/wasp/packages/vm/core/governance" "github.com/iotaledger/wasp/packages/webapi/model" "github.com/iotaledger/wasp/packages/webapi/routes" "github.com/iotaledger/wasp/tools/cluster/mocknode" @@ -34,10 +36,11 @@ import ( ) type Cluster struct { - Name string - Config *ClusterConfig - Started bool - DataPath string + Name string + Config *ClusterConfig + Started bool + DataPath string + ValidatorSeed *seed.Seed // Default identity for validators, chain owners, etc. goshimmer *mocknode.MockNode waspCmds []*exec.Cmd @@ -45,12 +48,17 @@ type Cluster struct { func New(name string, config *ClusterConfig) *Cluster { return &Cluster{ - Name: name, - Config: config, - waspCmds: make([]*exec.Cmd, config.Wasp.NumNodes), + Name: name, + Config: config, + ValidatorSeed: seed.NewSeed(), + waspCmds: make([]*exec.Cmd, config.Wasp.NumNodes), } } +func (clu *Cluster) ValidatorAddress() ledgerstate.Address { + return clu.ValidatorSeed.Address(0).Address() +} + func (clu *Cluster) NewKeyPairWithFunds() (*ed25519.KeyPair, ledgerstate.Address, error) { key, addr := testkey.GenKeyAddr() err := clu.GoshimmerClient().RequestFunds(addr) @@ -128,15 +136,13 @@ func (clu *Cluster) DeployChainWithDKG(description string, allPeers, committeeNo } func (clu *Cluster) DeployChain(description string, allPeers, committeeNodes []int, quorum uint16, stateAddr ledgerstate.Address) (*Chain, error) { - ownerSeed := seed.NewSeed() - if len(allPeers) == 0 { allPeers = clu.Config.AllNodes() } chain := &Chain{ Description: description, - OriginatorSeed: ownerSeed, + OriginatorSeed: clu.ValidatorSeed, AllPeers: allPeers, CommitteeNodes: committeeNodes, Quorum: quorum, @@ -159,7 +165,7 @@ func (clu *Cluster) DeployChain(description string, allPeers, committeeNodes []i committeePubKeys = append(committeePubKeys, peeringNode.PubKey) } - chainid, err := apilib.DeployChain(apilib.CreateChainParams{ + chainID, err := apilib.DeployChain(apilib.CreateChainParams{ Node: clu.GoshimmerClient(), CommitteeAPIHosts: chain.CommitteeAPIHosts(), CommitteePubKeys: committeePubKeys, @@ -175,11 +181,66 @@ func (clu *Cluster) DeployChain(description string, allPeers, committeeNodes []i } chain.StateAddress = stateAddr - chain.ChainID = chainid + chain.ChainID = chainID + + // + // Register all non-committee nodes as access nodes. + for _, a := range allPeers { + inCommittee := false + for _, c := range committeeNodes { + if c == a { + inCommittee = true + break + } + } + if inCommittee { + continue + } + if err := clu.AddAccessNode(a, chain); err != nil { + return nil, err + } + } return chain, nil } +// AddAccessNode introduces node at accessNodeIndex as an access node to the chain. +// This is done by activating the chain on the node and asking the governance contract +// to consider it as an access node. +func (clu *Cluster) AddAccessNode(accessNodeIndex int, chain *Chain) error { + waspClient := clu.WaspClient(accessNodeIndex) + if err := apilib.ActivateChainOnAccessNodes(clu.Config.APIHosts([]int{accessNodeIndex}), chain.ChainID); err != nil { + return err + } + accessNodePeering, err := waspClient.GetPeeringSelf() + if err != nil { + return err + } + accessNodePubKey, err := ed25519.PublicKeyFromString(accessNodePeering.PubKey) + if err != nil { + return err + } + cert, err := waspClient.NodeOwnershipCertificate(accessNodePubKey, chain.OriginatorAddress()) + if err != nil { + return err + } + scArgs := governance.AccessNodeInfo{ + NodePubKey: accessNodePubKey.Bytes(), + ValidatorAddr: chain.OriginatorAddress().Bytes(), + Certificate: cert.Bytes(), + ForCommittee: false, + AccessAPI: clu.Config.APIHost(accessNodeIndex), + } + scParams := chainclient.NewPostRequestParams(scArgs.ToAddCandidateNodeParams()).WithIotas(1) + govClient := chain.SCClient(governance.Contract.Hname(), chain.OriginatorKeyPair()) + tx, err := govClient.PostRequest(governance.FuncAddCandidateNode.Name, *scParams) + if err != nil { + return err + } + fmt.Printf("[cluster] Governance::AddCandidateNode, Posted TX, id=%v, args=%+v\n", tx.ID(), scArgs) + return nil +} + func (clu *Cluster) IsGoshimmerUp() bool { return clu.goshimmer != nil } @@ -235,7 +296,7 @@ func (clu *Cluster) InitDataPath(templatesPath, dataPath string, removeExisting waspNodeDataPath(dataPath, i), path.Join(templatesPath, "wasp-config-template.json"), templates.WaspConfig, - clu.Config.WaspConfigTemplateParams(i), + clu.Config.WaspConfigTemplateParams(i, clu.ValidatorAddress()), i, modifyConfig, ) diff --git a/tools/cluster/config.go b/tools/cluster/config.go index a9bfd3f57a..16a0f38d1b 100644 --- a/tools/cluster/config.go +++ b/tools/cluster/config.go @@ -5,8 +5,8 @@ import ( "fmt" "os" "path" - "strings" + "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/tools/cluster/templates" ) @@ -131,14 +131,6 @@ func (c *ClusterConfig) PeeringHosts(nodeIndexes ...[]int) []string { return c.waspHosts(nodes, func(i int) string { return c.PeeringHost(i) }) } -func (c *ClusterConfig) NeighborsString() string { - ret := make([]string, c.Wasp.NumNodes) - for i := range ret { - ret[i] = "\"" + c.PeeringHost(i) + "\"" - } - return strings.Join(ret, ",") -} - func (c *ClusterConfig) PeeringHost(nodeIndex int) string { return fmt.Sprintf("127.0.0.1:%d", c.PeeringPort(nodeIndex)) } @@ -189,17 +181,17 @@ func (c *ClusterConfig) PrometheusPort(nodeIndex int) int { return c.Wasp.FirstMetricsPort + nodeIndex } -func (c *ClusterConfig) WaspConfigTemplateParams(i int) *templates.WaspConfigParams { +func (c *ClusterConfig) WaspConfigTemplateParams(i int, ownerAddress ledgerstate.Address) *templates.WaspConfigParams { return &templates.WaspConfigParams{ APIPort: c.APIPort(i), DashboardPort: c.DashboardPort(i), PeeringPort: c.PeeringPort(i), NanomsgPort: c.NanomsgPort(i), - Neighbors: c.NeighborsString(), TxStreamPort: c.TxStreamPort(i), ProfilingPort: c.ProfilingPort(i), TxStreamHost: c.TxStreamHost(i), MetricsPort: c.PrometheusPort(i), + OwnerAddress: ownerAddress.Base58(), OffledgerBroadcastUpToNPeers: 10, } } diff --git a/tools/cluster/templates/waspconfig.go b/tools/cluster/templates/waspconfig.go index c5875d905b..183ad49239 100644 --- a/tools/cluster/templates/waspconfig.go +++ b/tools/cluster/templates/waspconfig.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package templates type WaspConfigParams struct { @@ -5,12 +8,12 @@ type WaspConfigParams struct { DashboardPort int PeeringPort int NanomsgPort int - Neighbors string TxStreamPort int TxStreamHost string ProfilingPort int MetricsPort int OffledgerBroadcastUpToNPeers int + OwnerAddress string } const WaspConfig = ` @@ -36,7 +39,8 @@ const WaspConfig = ` }, "node": { "disablePlugins": [], - "enablePlugins": [] + "enablePlugins": [], + "ownerAddresses": ["{{.OwnerAddress}}"] }, "webapi": { "bindAddress": "0.0.0.0:{{.APIPort}}" @@ -47,7 +51,6 @@ const WaspConfig = ` "peering":{ "port": {{.PeeringPort}}, "netid": "127.0.0.1:{{.PeeringPort}}", - "neighbors": [{{.Neighbors}}] }, "nodeconn": { "address": "{{.TxStreamHost}}:{{.TxStreamPort}}" From 77f630f6c77c942428840044ecb59c044a23e468 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Tue, 28 Dec 2021 21:29:52 +0200 Subject: [PATCH 023/120] Some references to NetIDs removed. --- packages/chain/chainimpl/messages.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/packages/chain/chainimpl/messages.go b/packages/chain/chainimpl/messages.go index 90e8ca5228..77a9452bf3 100644 --- a/packages/chain/chainimpl/messages.go +++ b/packages/chain/chainimpl/messages.go @@ -3,22 +3,7 @@ package chainimpl -import ( - "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/iscp/request" -) - // DismissChainMsg sent by component to the chain core in case of major setback type DismissChainMsg struct { Reason string } - -type OffLedgerRequestMsg struct { - Req *request.OffLedger - SenderNetID string -} - -type RequestAckMsg struct { - ReqID *iscp.RequestID - SenderNetID string -} From b90fff5e298106143931855d1ba38d4faf9060ea Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 29 Dec 2021 12:29:42 +0200 Subject: [PATCH 024/120] Consider the case when DKShare is not available. --- packages/chain/chainimpl/eventproc.go | 14 +++++++++++--- packages/registry/providers.go | 3 +++ packages/registry/registry_impl.go | 3 +++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/chain/chainimpl/eventproc.go b/packages/chain/chainimpl/eventproc.go index 8d3e61bc75..6282fa14b8 100644 --- a/packages/chain/chainimpl/eventproc.go +++ b/packages/chain/chainimpl/eventproc.go @@ -4,6 +4,7 @@ package chainimpl import ( + "errors" "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" @@ -16,6 +17,7 @@ import ( "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/iscp/request" "github.com/iotaledger/wasp/packages/peering" + "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/tcrypto" "golang.org/x/xerrors" ) @@ -140,7 +142,10 @@ func (c *chainObj) rotateCommitteeIfNeeded(anchorOutput *ledgerstate.AliasOutput } dkShare, err := c.getChainDKShare(anchorOutput.GetStateAddress()) if err != nil { - return xerrors.Errorf("rotateCommitteeIfNeeded: %w", err) + if errors.Is(err, registry.ErrDKShareNotFound) { + return nil + } + return xerrors.Errorf("rotateCommitteeIfNeeded: unable to load dkShare: %w", err) } // rotation needed // close current in any case @@ -163,12 +168,15 @@ func (c *chainObj) createCommitteeIfNeeded(anchorOutput *ledgerstate.AliasOutput // check if I am in the committee dkShare, err := c.getChainDKShare(anchorOutput.GetStateAddress()) if err != nil { - return xerrors.Errorf("rotateCommitteeIfNeeded: %w", err) + if errors.Is(err, registry.ErrDKShareNotFound) { + return nil + } + return xerrors.Errorf("createCommitteeIfNeeded: unable to load dkShare: %w", err) } if dkShare != nil { // create if record is present if err = c.createNewCommitteeAndConsensus(dkShare); err != nil { - return xerrors.Errorf("rotateCommitteeIfNeeded: creating committee and consensus: %v", err) + return xerrors.Errorf("createCommitteeIfNeeded: creating committee and consensus: %w", err) } } return nil diff --git a/packages/registry/providers.go b/packages/registry/providers.go index b484583b12..5992085d00 100644 --- a/packages/registry/providers.go +++ b/packages/registry/providers.go @@ -4,6 +4,7 @@ package registry import ( + "errors" "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" @@ -35,6 +36,8 @@ type DKShareRegistryProvider interface { LoadDKShare(sharedAddress ledgerstate.Address) (*tcrypto.DKShare, error) } +var ErrDKShareNotFound = errors.New("dkShare not found") + // ChainRecordRegistryProvider stands for a partial registry interface, needed for this package. type ChainRecordRegistryProvider interface { GetChainRecordByChainID(chainID *iscp.ChainID) (*ChainRecord, error) diff --git a/packages/registry/registry_impl.go b/packages/registry/registry_impl.go index 525d823a2c..bb48c3cfdf 100644 --- a/packages/registry/registry_impl.go +++ b/packages/registry/registry_impl.go @@ -133,6 +133,9 @@ func (r *Impl) SaveDKShare(dkShare *tcrypto.DKShare) error { func (r *Impl) LoadDKShare(sharedAddress ledgerstate.Address) (*tcrypto.DKShare, error) { data, err := r.store.Get(dbKeyForDKShare(sharedAddress)) if err != nil { + if err.Error() == "key not found" { + return nil, ErrDKShareNotFound + } return nil, err } return tcrypto.DKShareFromBytes(data, tcrypto.DefaultSuite()) From a95e32e78cee073400c1360b19e819f7c7bbacc2 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 29 Dec 2021 11:57:23 -0800 Subject: [PATCH 025/120] Initial working go client code --- .../go/testwasmlibclient/service.go | 172 +++++++++--------- .../wasm/testwasmlib/test/testwasmlib_test.go | 31 +++- .../vm/wasmlib/go/wasmclient/arguments.go | 46 ++--- .../vm/wasmlib/go/wasmclient/clientfunc.go | 21 +++ .../vm/wasmlib/go/wasmclient/clientview.go | 9 + packages/vm/wasmlib/go/wasmclient/event.go | 4 +- packages/vm/wasmlib/go/wasmclient/service.go | 100 ++++++---- .../vm/wasmlib/go/wasmclient/serviceclient.go | 16 ++ .../generator/goclienttemplates/service.go | 20 +- 9 files changed, 264 insertions(+), 155 deletions(-) create mode 100644 packages/vm/wasmlib/go/wasmclient/clientfunc.go create mode 100644 packages/vm/wasmlib/go/wasmclient/clientview.go create mode 100644 packages/vm/wasmlib/go/wasmclient/serviceclient.go diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go index c0157362d2..cd3f8283bc 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go @@ -46,318 +46,318 @@ const ( ///////////////////////////// arrayClear ///////////////////////////// type ArrayClearFunc struct { - svc *wasmclient.Service + wasmclient.ClientFunc args wasmclient.Arguments } -func (f ArrayClearFunc) Name(v string) { +func (f *ArrayClearFunc) Name(v string) { f.args.SetString(ArgName, v) } -func (f ArrayClearFunc) Post(transfer ...map[string]uint64) wasmclient.Request { +func (f *ArrayClearFunc) Post() wasmclient.Request { f.args.Mandatory(ArgName) - return f.svc.PostRequest(0x88021821, &f.args, transfer...) + return f.ClientFunc.Post(0x88021821, &f.args) } ///////////////////////////// arrayCreate ///////////////////////////// type ArrayCreateFunc struct { - svc *wasmclient.Service + wasmclient.ClientFunc args wasmclient.Arguments } -func (f ArrayCreateFunc) Name(v string) { +func (f *ArrayCreateFunc) Name(v string) { f.args.SetString(ArgName, v) } -func (f ArrayCreateFunc) Post(transfer ...map[string]uint64) wasmclient.Request { +func (f *ArrayCreateFunc) Post() wasmclient.Request { f.args.Mandatory(ArgName) - return f.svc.PostRequest(0x1ed5b23b, &f.args, transfer...) + return f.ClientFunc.Post(0x1ed5b23b, &f.args) } ///////////////////////////// arraySet ///////////////////////////// type ArraySetFunc struct { - svc *wasmclient.Service + wasmclient.ClientFunc args wasmclient.Arguments } -func (f ArraySetFunc) Index(v int32) { +func (f *ArraySetFunc) Index(v int32) { f.args.SetInt32(ArgIndex, v) } -func (f ArraySetFunc) Name(v string) { +func (f *ArraySetFunc) Name(v string) { f.args.SetString(ArgName, v) } -func (f ArraySetFunc) Value(v string) { +func (f *ArraySetFunc) Value(v string) { f.args.SetString(ArgValue, v) } -func (f ArraySetFunc) Post(transfer ...map[string]uint64) wasmclient.Request { +func (f *ArraySetFunc) Post() wasmclient.Request { f.args.Mandatory(ArgIndex) f.args.Mandatory(ArgName) f.args.Mandatory(ArgValue) - return f.svc.PostRequest(0x2c4150b3, &f.args, transfer...) + return f.ClientFunc.Post(0x2c4150b3, &f.args) } ///////////////////////////// paramTypes ///////////////////////////// type ParamTypesFunc struct { - svc *wasmclient.Service + wasmclient.ClientFunc args wasmclient.Arguments } -func (f ParamTypesFunc) Address(v wasmclient.Address) { +func (f *ParamTypesFunc) Address(v wasmclient.Address) { f.args.SetAddress(ArgAddress, v) } -func (f ParamTypesFunc) AgentID(v wasmclient.AgentID) { +func (f *ParamTypesFunc) AgentID(v wasmclient.AgentID) { f.args.SetAgentID(ArgAgentID, v) } -func (f ParamTypesFunc) Bool(v bool) { +func (f *ParamTypesFunc) Bool(v bool) { f.args.SetBool(ArgBool, v) } -func (f ParamTypesFunc) Bytes(v []byte) { +func (f *ParamTypesFunc) Bytes(v []byte) { f.args.SetBytes(ArgBytes, v) } -func (f ParamTypesFunc) ChainID(v wasmclient.ChainID) { +func (f *ParamTypesFunc) ChainID(v wasmclient.ChainID) { f.args.SetChainID(ArgChainID, v) } -func (f ParamTypesFunc) Color(v wasmclient.Color) { +func (f *ParamTypesFunc) Color(v wasmclient.Color) { f.args.SetColor(ArgColor, v) } -func (f ParamTypesFunc) Hash(v wasmclient.Hash) { +func (f *ParamTypesFunc) Hash(v wasmclient.Hash) { f.args.SetHash(ArgHash, v) } -func (f ParamTypesFunc) Hname(v wasmclient.Hname) { +func (f *ParamTypesFunc) Hname(v wasmclient.Hname) { f.args.SetHname(ArgHname, v) } -func (f ParamTypesFunc) Int16(v int16) { +func (f *ParamTypesFunc) Int16(v int16) { f.args.SetInt16(ArgInt16, v) } -func (f ParamTypesFunc) Int32(v int32) { +func (f *ParamTypesFunc) Int32(v int32) { f.args.SetInt32(ArgInt32, v) } -func (f ParamTypesFunc) Int64(v int64) { +func (f *ParamTypesFunc) Int64(v int64) { f.args.SetInt64(ArgInt64, v) } -func (f ParamTypesFunc) Int8(v int8) { +func (f *ParamTypesFunc) Int8(v int8) { f.args.SetInt8(ArgInt8, v) } -func (f ParamTypesFunc) Param(v []byte) { +func (f *ParamTypesFunc) Param(v []byte) { f.args.SetBytes(ArgParam, v) } -func (f ParamTypesFunc) RequestID(v wasmclient.RequestID) { +func (f *ParamTypesFunc) RequestID(v wasmclient.RequestID) { f.args.SetRequestID(ArgRequestID, v) } -func (f ParamTypesFunc) String(v string) { +func (f *ParamTypesFunc) String(v string) { f.args.SetString(ArgString, v) } -func (f ParamTypesFunc) Uint16(v uint16) { +func (f *ParamTypesFunc) Uint16(v uint16) { f.args.SetUint16(ArgUint16, v) } -func (f ParamTypesFunc) Uint32(v uint32) { +func (f *ParamTypesFunc) Uint32(v uint32) { f.args.SetUint32(ArgUint32, v) } -func (f ParamTypesFunc) Uint64(v uint64) { +func (f *ParamTypesFunc) Uint64(v uint64) { f.args.SetUint64(ArgUint64, v) } -func (f ParamTypesFunc) Uint8(v uint8) { +func (f *ParamTypesFunc) Uint8(v uint8) { f.args.SetUint8(ArgUint8, v) } -func (f ParamTypesFunc) Post(transfer ...map[string]uint64) wasmclient.Request { - return f.svc.PostRequest(0x6921c4cd, &f.args, transfer...) +func (f *ParamTypesFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0x6921c4cd, &f.args) } ///////////////////////////// random ///////////////////////////// type RandomFunc struct { - svc *wasmclient.Service + wasmclient.ClientFunc } -func (f RandomFunc) Post(transfer ...map[string]uint64) wasmclient.Request { - return f.svc.PostRequest(0xe86c97ca, nil, transfer...) +func (f *RandomFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0xe86c97ca, nil) } ///////////////////////////// triggerEvent ///////////////////////////// type TriggerEventFunc struct { - svc *wasmclient.Service + wasmclient.ClientFunc args wasmclient.Arguments } -func (f TriggerEventFunc) Address(v wasmclient.Address) { +func (f *TriggerEventFunc) Address(v wasmclient.Address) { f.args.SetAddress(ArgAddress, v) } -func (f TriggerEventFunc) Name(v string) { +func (f *TriggerEventFunc) Name(v string) { f.args.SetString(ArgName, v) } -func (f TriggerEventFunc) Post(transfer ...map[string]uint64) wasmclient.Request { +func (f *TriggerEventFunc) Post() wasmclient.Request { f.args.Mandatory(ArgAddress) f.args.Mandatory(ArgName) - return f.svc.PostRequest(0xd5438ac6, &f.args, transfer...) + return f.ClientFunc.Post(0xd5438ac6, &f.args) } ///////////////////////////// arrayLength ///////////////////////////// type ArrayLengthView struct { - svc *wasmclient.Service + wasmclient.ClientView args wasmclient.Arguments } -func (f ArrayLengthView) Name(v string) { +func (f *ArrayLengthView) Name(v string) { f.args.SetString(ArgName, v) } -func (f ArrayLengthView) Call() ArrayLengthResults { +func (f *ArrayLengthView) Call() ArrayLengthResults { f.args.Mandatory(ArgName) - return ArrayLengthResults{res: f.svc.CallView("arrayLength", &f.args)} + return ArrayLengthResults{res: f.ClientView.Call("arrayLength", &f.args)} } type ArrayLengthResults struct { res wasmclient.Results } -func (r ArrayLengthResults) Length() int32 { +func (r *ArrayLengthResults) Length() int32 { return r.res.GetInt32(ResLength) } ///////////////////////////// arrayValue ///////////////////////////// type ArrayValueView struct { - svc *wasmclient.Service + wasmclient.ClientView args wasmclient.Arguments } -func (f ArrayValueView) Index(v int32) { +func (f *ArrayValueView) Index(v int32) { f.args.SetInt32(ArgIndex, v) } -func (f ArrayValueView) Name(v string) { +func (f *ArrayValueView) Name(v string) { f.args.SetString(ArgName, v) } -func (f ArrayValueView) Call() ArrayValueResults { +func (f *ArrayValueView) Call() ArrayValueResults { f.args.Mandatory(ArgIndex) f.args.Mandatory(ArgName) - return ArrayValueResults{res: f.svc.CallView("arrayValue", &f.args)} + return ArrayValueResults{res: f.ClientView.Call("arrayValue", &f.args)} } type ArrayValueResults struct { res wasmclient.Results } -func (r ArrayValueResults) Value() string { +func (r *ArrayValueResults) Value() string { return r.res.GetString(ResValue) } ///////////////////////////// blockRecord ///////////////////////////// type BlockRecordView struct { - svc *wasmclient.Service + wasmclient.ClientView args wasmclient.Arguments } -func (f BlockRecordView) BlockIndex(v int32) { +func (f *BlockRecordView) BlockIndex(v int32) { f.args.SetInt32(ArgBlockIndex, v) } -func (f BlockRecordView) RecordIndex(v int32) { +func (f *BlockRecordView) RecordIndex(v int32) { f.args.SetInt32(ArgRecordIndex, v) } -func (f BlockRecordView) Call() BlockRecordResults { +func (f *BlockRecordView) Call() BlockRecordResults { f.args.Mandatory(ArgBlockIndex) f.args.Mandatory(ArgRecordIndex) - return BlockRecordResults{res: f.svc.CallView("blockRecord", &f.args)} + return BlockRecordResults{res: f.ClientView.Call("blockRecord", &f.args)} } type BlockRecordResults struct { res wasmclient.Results } -func (r BlockRecordResults) Record() []byte { +func (r *BlockRecordResults) Record() []byte { return r.res.GetBytes(ResRecord) } ///////////////////////////// blockRecords ///////////////////////////// type BlockRecordsView struct { - svc *wasmclient.Service + wasmclient.ClientView args wasmclient.Arguments } -func (f BlockRecordsView) BlockIndex(v int32) { +func (f *BlockRecordsView) BlockIndex(v int32) { f.args.SetInt32(ArgBlockIndex, v) } -func (f BlockRecordsView) Call() BlockRecordsResults { +func (f *BlockRecordsView) Call() BlockRecordsResults { f.args.Mandatory(ArgBlockIndex) - return BlockRecordsResults{res: f.svc.CallView("blockRecords", &f.args)} + return BlockRecordsResults{res: f.ClientView.Call("blockRecords", &f.args)} } type BlockRecordsResults struct { res wasmclient.Results } -func (r BlockRecordsResults) Count() int32 { +func (r *BlockRecordsResults) Count() int32 { return r.res.GetInt32(ResCount) } ///////////////////////////// getRandom ///////////////////////////// type GetRandomView struct { - svc *wasmclient.Service + wasmclient.ClientView } -func (f GetRandomView) Call() GetRandomResults { - return GetRandomResults{res: f.svc.CallView("getRandom", nil)} +func (f *GetRandomView) Call() GetRandomResults { + return GetRandomResults{res: f.ClientView.Call("getRandom", nil)} } type GetRandomResults struct { res wasmclient.Results } -func (r GetRandomResults) Random() int64 { +func (r *GetRandomResults) Random() int64 { return r.res.GetInt64(ResRandom) } ///////////////////////////// iotaBalance ///////////////////////////// type IotaBalanceView struct { - svc *wasmclient.Service + wasmclient.ClientView } -func (f IotaBalanceView) Call() IotaBalanceResults { - return IotaBalanceResults{res: f.svc.CallView("iotaBalance", nil)} +func (f *IotaBalanceView) Call() IotaBalanceResults { + return IotaBalanceResults{res: f.ClientView.Call("iotaBalance", nil)} } type IotaBalanceResults struct { res wasmclient.Results } -func (r IotaBalanceResults) Iotas() int64 { +func (r *IotaBalanceResults) Iotas() int64 { return r.res.GetInt64(ResIotas) } @@ -374,49 +374,49 @@ func NewTestWasmLibService(cl *wasmclient.ServiceClient, chainID string) (*TestW } func (s *TestWasmLibService) ArrayClear() ArrayClearFunc { - return ArrayClearFunc{svc: &s.Service} + return ArrayClearFunc{ClientFunc: s.AsClientFunc()} } func (s *TestWasmLibService) ArrayCreate() ArrayCreateFunc { - return ArrayCreateFunc{svc: &s.Service} + return ArrayCreateFunc{ClientFunc: s.AsClientFunc()} } func (s *TestWasmLibService) ArraySet() ArraySetFunc { - return ArraySetFunc{svc: &s.Service} + return ArraySetFunc{ClientFunc: s.AsClientFunc()} } func (s *TestWasmLibService) ParamTypes() ParamTypesFunc { - return ParamTypesFunc{svc: &s.Service} + return ParamTypesFunc{ClientFunc: s.AsClientFunc()} } func (s *TestWasmLibService) Random() RandomFunc { - return RandomFunc{svc: &s.Service} + return RandomFunc{ClientFunc: s.AsClientFunc()} } func (s *TestWasmLibService) TriggerEvent() TriggerEventFunc { - return TriggerEventFunc{svc: &s.Service} + return TriggerEventFunc{ClientFunc: s.AsClientFunc()} } func (s *TestWasmLibService) ArrayLength() ArrayLengthView { - return ArrayLengthView{svc: &s.Service} + return ArrayLengthView{ClientView: s.AsClientView()} } func (s *TestWasmLibService) ArrayValue() ArrayValueView { - return ArrayValueView{svc: &s.Service} + return ArrayValueView{ClientView: s.AsClientView()} } func (s *TestWasmLibService) BlockRecord() BlockRecordView { - return BlockRecordView{svc: &s.Service} + return BlockRecordView{ClientView: s.AsClientView()} } func (s *TestWasmLibService) BlockRecords() BlockRecordsView { - return BlockRecordsView{svc: &s.Service} + return BlockRecordsView{ClientView: s.AsClientView()} } func (s *TestWasmLibService) GetRandom() GetRandomView { - return GetRandomView{svc: &s.Service} + return GetRandomView{ClientView: s.AsClientView()} } func (s *TestWasmLibService) IotaBalance() IotaBalanceView { - return IotaBalanceView{svc: &s.Service} + return IotaBalanceView{ClientView: s.AsClientView()} } diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_test.go index 5b41c1b690..6c0271d059 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_test.go @@ -6,12 +6,15 @@ import ( "strings" "testing" + "github.com/iotaledger/goshimmer/client/wallet/packages/seed" "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlib" + "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlibclient" "github.com/iotaledger/wasp/packages/solo" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/iotaledger/wasp/packages/vm/wasmsolo" + "github.com/mr-tron/base58" "github.com/stretchr/testify/require" ) @@ -309,5 +312,31 @@ func TestMultiRandom(t *testing.T) { } func TestClient(t *testing.T) { - _ = wasmclient.ServiceClient{} + seedBytes, err := base58.Decode("6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv") + require.NoError(t, err) + mySeed := seed.NewSeed(seedBytes) + keyPair := mySeed.KeyPair(0) + ed25519Address := ledgerstate.NewED25519Address(keyPair.PublicKey) + address := wasmclient.Address(base58.Encode(ed25519Address.Bytes())) + + cl := wasmclient.DefaultServiceClient() + svc, err := testwasmlibclient.NewTestWasmLibService(cl, "qjA8Ybw4WijnmGUqDtNcPhAxFymjQKepNyyfp5BUGsWP") + require.NoError(t, err) + svc.SignRequests(keyPair) + + f := svc.TriggerEvent() + f.Name("Lala") + f.Address(address) + req := f.Post() + require.NoError(t, req.Error()) + err = svc.WaitRequest(req) + require.NoError(t, err) + + f = svc.TriggerEvent() + f.Name("Trala") + f.Address(address) + req = f.Post() + require.NoError(t, req.Error()) + err = svc.WaitRequest(req) + require.NoError(t, err) } diff --git a/packages/vm/wasmlib/go/wasmclient/arguments.go b/packages/vm/wasmlib/go/wasmclient/arguments.go index 9a9fc875f6..e63f5d784b 100644 --- a/packages/vm/wasmlib/go/wasmclient/arguments.go +++ b/packages/vm/wasmlib/go/wasmclient/arguments.go @@ -14,14 +14,14 @@ type Arguments struct { args dict.Dict } -func (a Arguments) set(key string, val []byte) { +func (a *Arguments) set(key string, val []byte) { if a.args == nil { a.args = make(dict.Dict) } a.args[kv.Key(key)] = val } -func (a Arguments) setBase58(key, val string, typeID int32) { +func (a *Arguments) setBase58(key, val string, typeID int32) { bytes := Base58Decode(val) if len(bytes) != int(wasmlib.TypeSizes[typeID]) { panic("invalid byte size") @@ -29,11 +29,11 @@ func (a Arguments) setBase58(key, val string, typeID int32) { a.set(key, bytes) } -func (a Arguments) IndexedKey(key string, index int) string { +func (a *Arguments) IndexedKey(key string, index int) string { return key + "." + strconv.Itoa(index) } -func (a Arguments) Mandatory(key string) { +func (a *Arguments) Mandatory(key string) { if a.args != nil { if _, ok := a.args[kv.Key(key)]; ok { return @@ -42,15 +42,15 @@ func (a Arguments) Mandatory(key string) { panic("missing mandatory " + key) } -func (a Arguments) SetAddress(key string, val Address) { +func (a *Arguments) SetAddress(key string, val Address) { a.setBase58(key, string(val), wasmlib.TYPE_ADDRESS) } -func (a Arguments) SetAgentID(key string, val AgentID) { +func (a *Arguments) SetAgentID(key string, val AgentID) { a.setBase58(key, string(val), wasmlib.TYPE_AGENT_ID) } -func (a Arguments) SetBool(key string, val bool) { +func (a *Arguments) SetBool(key string, val bool) { bytes := []byte{0} if val { bytes[0] = 1 @@ -58,67 +58,67 @@ func (a Arguments) SetBool(key string, val bool) { a.set(key, bytes) } -func (a Arguments) SetBytes(key string, val []byte) { +func (a *Arguments) SetBytes(key string, val []byte) { a.set(key, val) } -func (a Arguments) SetColor(key string, val Color) { +func (a *Arguments) SetColor(key string, val Color) { a.setBase58(key, string(val), wasmlib.TYPE_COLOR) } -func (a Arguments) SetChainID(key string, val ChainID) { +func (a *Arguments) SetChainID(key string, val ChainID) { a.setBase58(key, string(val), wasmlib.TYPE_CHAIN_ID) } -func (a Arguments) SetHash(key string, val Hash) { +func (a *Arguments) SetHash(key string, val Hash) { a.setBase58(key, string(val), wasmlib.TYPE_HASH) } -func (a Arguments) SetHname(key string, val Hname) { +func (a *Arguments) SetHname(key string, val Hname) { a.SetUint32(key, uint32(val)) } -func (a Arguments) SetInt8(key string, val int8) { +func (a *Arguments) SetInt8(key string, val int8) { a.set(key, []byte{byte(val)}) } -func (a Arguments) SetInt16(key string, val int16) { +func (a *Arguments) SetInt16(key string, val int16) { a.setUint64(key, uint64(val), 2) } -func (a Arguments) SetInt32(key string, val int32) { +func (a *Arguments) SetInt32(key string, val int32) { a.setUint64(key, uint64(val), 4) } -func (a Arguments) SetInt64(key string, val int64) { +func (a *Arguments) SetInt64(key string, val int64) { a.setUint64(key, uint64(val), 4) } -func (a Arguments) SetRequestID(key string, val RequestID) { +func (a *Arguments) SetRequestID(key string, val RequestID) { a.setBase58(key, string(val), wasmlib.TYPE_REQUEST_ID) } -func (a Arguments) SetString(key, val string) { +func (a *Arguments) SetString(key, val string) { a.set(key, []byte(val)) } -func (a Arguments) SetUint8(key string, val uint8) { +func (a *Arguments) SetUint8(key string, val uint8) { a.set(key, []byte{val}) } -func (a Arguments) SetUint16(key string, val uint16) { +func (a *Arguments) SetUint16(key string, val uint16) { a.setUint64(key, uint64(val), 2) } -func (a Arguments) SetUint32(key string, val uint32) { +func (a *Arguments) SetUint32(key string, val uint32) { a.setUint64(key, uint64(val), 4) } -func (a Arguments) SetUint64(key string, val uint64) { +func (a *Arguments) SetUint64(key string, val uint64) { a.setUint64(key, val, 4) } -func (a Arguments) setUint64(key string, val uint64, size int) { +func (a *Arguments) setUint64(key string, val uint64, size int) { bytes := make([]byte, size) for i := 0; i < size; i++ { bytes[i] = byte(val) diff --git a/packages/vm/wasmlib/go/wasmclient/clientfunc.go b/packages/vm/wasmlib/go/wasmclient/clientfunc.go new file mode 100644 index 0000000000..8917b8e8a1 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/clientfunc.go @@ -0,0 +1,21 @@ +package wasmclient + +import "github.com/iotaledger/hive.go/crypto/ed25519" + +type ClientFunc struct { + svc *Service + keyPair *ed25519.KeyPair + transfer map[string]uint64 +} + +func (f *ClientFunc) Post(funcHname uint32, args *Arguments) Request { + keyPair := f.keyPair + if keyPair == nil { + keyPair = f.svc.keyPair + } + return f.svc.PostRequest(funcHname, args, f.transfer, keyPair) +} + +func (f *ClientFunc) Sign(keyPair *ed25519.KeyPair) { + f.keyPair = keyPair +} diff --git a/packages/vm/wasmlib/go/wasmclient/clientview.go b/packages/vm/wasmlib/go/wasmclient/clientview.go new file mode 100644 index 0000000000..e6e5c02983 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/clientview.go @@ -0,0 +1,9 @@ +package wasmclient + +type ClientView struct { + svc *Service +} + +func (f *ClientView) Call(viewName string, args *Arguments) Results { + return f.svc.CallView(viewName, args) +} diff --git a/packages/vm/wasmlib/go/wasmclient/event.go b/packages/vm/wasmlib/go/wasmclient/event.go index c452801269..93c7a4246b 100644 --- a/packages/vm/wasmlib/go/wasmclient/event.go +++ b/packages/vm/wasmlib/go/wasmclient/event.go @@ -5,11 +5,13 @@ import ( ) type Event struct { - message []string + message []string + Timestamp uint32 } func (e *Event) Init(message []string) { e.message = message + e.Timestamp = e.NextUint32() } func (e *Event) next() string { diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go index 2f8ab16288..52f3126267 100644 --- a/packages/vm/wasmlib/go/wasmclient/service.go +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -1,7 +1,8 @@ package wasmclient import ( - "errors" + "fmt" + "strings" "time" "github.com/iotaledger/hive.go/crypto/ed25519" @@ -10,44 +11,52 @@ import ( "github.com/iotaledger/wasp/packages/iscp/colored" "github.com/iotaledger/wasp/packages/iscp/request" "github.com/iotaledger/wasp/packages/iscp/requestargs" + "github.com/iotaledger/wasp/packages/subscribe" "github.com/mr-tron/base58" ) -type ServiceClient struct { - client *client.WaspClient - keyPair *ed25519.KeyPair -} - type Response map[string][]byte type Service struct { - chainID *iscp.ChainID - cl *ServiceClient - scHname iscp.Hname + chainID *iscp.ChainID + keyPair *ed25519.KeyPair + scHname iscp.Hname + waspClient *client.WaspClient } -func (s *Service) Init(cl *ServiceClient, chainID string, scHname uint32, eventHandlers map[string]func([]string)) (err error) { +func (s *Service) Init(svcClient *ServiceClient, chainID string, scHname uint32, eventHandlers map[string]func([]string)) (err error) { + s.waspClient = svcClient.waspClient s.scHname = iscp.Hname(scHname) s.chainID, err = iscp.ChainIDFromString(chainID) - return err + if err != nil { + return err + } + return s.startEventHandlers(svcClient.eventPort, eventHandlers) +} + +func (s *Service) AsClientFunc() ClientFunc { + return ClientFunc{svc: s} +} + +func (s *Service) AsClientView() ClientView { + return ClientView{svc: s} } func (s *Service) CallView(viewName string, args *Arguments) (ret Results) { - ret.res, ret.err = s.cl.client.CallView(s.chainID, s.scHname, viewName, args.args) + ret.res, ret.err = s.waspClient.CallView(s.chainID, s.scHname, viewName, args.args) return ret } -func (s *Service) PostRequest(funcHname uint32, args *Arguments, transfer ...map[string]uint64) Request { - bal, err := makeBalances(transfer...) +func (s *Service) PostRequest(funcHname uint32, args *Arguments, transfer map[string]uint64, keyPair *ed25519.KeyPair) Request { + bal, err := makeBalances(transfer) if err != nil { return Request{err: err} } reqArgs := requestargs.New().AddEncodeSimpleMany(args.args) req := request.NewOffLedger(s.chainID, s.scHname, iscp.Hname(funcHname), reqArgs) req.WithTransfer(bal) - req.WithNonce(uint64(time.Now().UnixNano())) - req.Sign(s.cl.keyPair) - err = s.cl.client.PostOffLedgerRequest(s.chainID, req) + req.Sign(keyPair) + err = s.waspClient.PostOffLedgerRequest(s.chainID, req) if err != nil { return Request{err: err} } @@ -55,8 +64,37 @@ func (s *Service) PostRequest(funcHname uint32, args *Arguments, transfer ...map return Request{id: &id} } +func (s *Service) SignRequests(keyPair *ed25519.KeyPair) { + s.keyPair = keyPair +} + func (s *Service) WaitRequest(req Request) error { - return s.cl.client.WaitUntilRequestProcessed(s.chainID, *req.id, 1*time.Minute) + return s.waspClient.WaitUntilRequestProcessed(s.chainID, *req.id, 1*time.Minute) +} + +func (s *Service) startEventHandlers(ep string, handlers map[string]func([]string)) error { + chMsg := make(chan []string) + chDone := make(chan bool) + err := subscribe.Subscribe(ep, chMsg, chDone, true, "") + if err != nil { + return err + } + go func() { + for { + for msgSplit := range chMsg { + event := strings.Join(msgSplit, " ") + fmt.Printf("%s\n", event) + if msgSplit[0] == "vmmsg" { + msg := strings.Split(msgSplit[3], "|") + handler, ok := handlers[msg[0]] + if ok { + handler(msg[1:]) + } + } + } + } + }() + return nil } ///////////////////////////////////////////////////////////////// @@ -73,24 +111,18 @@ func Base58Encode(b []byte) string { return base58.Encode(b) } -func makeBalances(transfer ...map[string]uint64) (colored.Balances, error) { +func makeBalances(transfer map[string]uint64) (colored.Balances, error) { cb := colored.NewBalances() - switch len(transfer) { - case 0: - case 1: - for color, amount := range transfer[0] { - if color == colored.IOTA.String() { - cb.Set(colored.IOTA, amount) - continue - } - c, err := colored.ColorFromBase58EncodedString(color) - if err != nil { - return nil, err - } - cb.Set(c, amount) + for color, amount := range transfer { + if color == colored.IOTA.String() { + cb.Set(colored.IOTA, amount) + continue + } + c, err := colored.ColorFromBase58EncodedString(color) + if err != nil { + return nil, err } - default: - return cb, errors.New("makeBalances: too many transfers") + cb.Set(c, amount) } return cb, nil } diff --git a/packages/vm/wasmlib/go/wasmclient/serviceclient.go b/packages/vm/wasmlib/go/wasmclient/serviceclient.go new file mode 100644 index 0000000000..4dd3777850 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/serviceclient.go @@ -0,0 +1,16 @@ +package wasmclient + +import "github.com/iotaledger/wasp/client" + +type ServiceClient struct { + waspClient *client.WaspClient + eventPort string +} + +func NewServiceClient(waspAPI, eventPort string) *ServiceClient { + return &ServiceClient{waspClient: client.NewWaspClient(waspAPI), eventPort: eventPort} +} + +func DefaultServiceClient() *ServiceClient { + return NewServiceClient("127.0.0.1:9090", "127.0.0.1:5550") +} diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index 7f6e9f9dc9..2dc5ba0734 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -39,7 +39,7 @@ $#each func serviceFunction ///////////////////////////// $funcName ///////////////////////////// type $FuncName$Kind struct { - svc *wasmclient.Service + wasmclient.Client$Kind $#if param funcArgsMember } $#each param funcArgSetter @@ -56,14 +56,14 @@ $#if array funcArgSetterArray funcArgSetterBasic // ******************************* "funcArgSetterBasic": ` -func (f $FuncName$Kind) $FldName(v $fldLangType) { +func (f *$FuncName$Kind) $FldName(v $fldLangType) { f.args.Set$FldType(Arg$FldName, v) } `, // ******************************* "funcArgSetterArray": ` -func (f $FuncName$Kind) $FldName(a []$fldLangType) { +func (f *$FuncName$Kind) $FldName(a []$fldLangType) { for i, v := range a { f.args.Set$FldType(f.args.IndexedKey(Arg$FldName, i), v) } @@ -73,19 +73,19 @@ func (f $FuncName$Kind) $FldName(a []$fldLangType) { // ******************************* "funcPost": ` -func (f $FuncName$Kind) Post(transfer ...map[string]uint64) wasmclient.Request { +func (f *$FuncName$Kind) Post() wasmclient.Request { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs - return f.svc.PostRequest(0x$funcHname, $args, transfer...) + return f.ClientFunc.Post(0x$funcHname, $args) } `, // ******************************* "viewCall": ` -func (f $FuncName$Kind) Call() $FuncName$+Results { +func (f *$FuncName$Kind) Call() $FuncName$+Results { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs - return $FuncName$+Results { res: f.svc.CallView("$funcName", $args) } + return $FuncName$+Results { res: f.ClientView.Call("$funcName", $args) } } $#if result resultStruct `, @@ -113,14 +113,14 @@ $#each result callResultGetter "callResultGetter": ` $#if mandatory else callResultOptional -func (r $FuncName$+Results) $FldName() $fldLangType { +func (r *$FuncName$+Results) $FldName() $fldLangType { return r.res.Get$FldType(Res$FldName) } `, // ******************************* "callResultOptional": ` -func (r $FuncName$+Results) $FldName$+Exists() bool { +func (r *$FuncName$+Results) $FldName$+Exists() bool { return r.res.Exists(Res$FldName) } `, @@ -138,7 +138,7 @@ func (r $FuncName$+Results) $FldName$+Exists() bool { "serviceFunction": ` func (s *$PkgName$+Service) $FuncName() $FuncName$Kind { - return $FuncName$Kind{ svc: &s.Service } + return $FuncName$Kind{ Client$Kind: s.AsClient$Kind() } } `, } From 11eb50ef5de76c97dd782e07646d9a32439df33e Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 29 Dec 2021 12:53:10 -0800 Subject: [PATCH 026/120] Smoothing out interface --- .../wasm/testwasmlib/test/testwasmlib_test.go | 53 ++++++++++++------- .../vm/wasmlib/go/wasmclient/clientfunc.go | 11 ++++ packages/vm/wasmlib/go/wasmclient/seed.go | 34 ++++++++++++ packages/vm/wasmlib/go/wasmclient/service.go | 2 +- 4 files changed, 79 insertions(+), 21 deletions(-) create mode 100644 packages/vm/wasmlib/go/wasmclient/seed.go diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_test.go index 6c0271d059..4ac57772f1 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_test.go @@ -5,8 +5,8 @@ import ( "strconv" "strings" "testing" + "time" - "github.com/iotaledger/goshimmer/client/wallet/packages/seed" "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlib" "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlibclient" @@ -14,7 +14,6 @@ import ( "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/iotaledger/wasp/packages/vm/wasmsolo" - "github.com/mr-tron/base58" "github.com/stretchr/testify/require" ) @@ -312,31 +311,45 @@ func TestMultiRandom(t *testing.T) { } func TestClient(t *testing.T) { - seedBytes, err := base58.Decode("6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv") + // hardcoded seed and chain ID, taken from wasp-cli.json + // note that normally the chain has already been set up and + // the contract has already been deployed in some way + const mySeed = "6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv" + require.True(t, wasmclient.SeedIsValid(mySeed)) + const myChainID = "qjA8Ybw4WijnmGUqDtNcPhAxFymjQKepNyyfp5BUGsWP" + require.True(t, wasmclient.ChainIsValid(myChainID)) + + // we're testing against wasp-cluster, so defaults will do + svcClient := wasmclient.DefaultServiceClient() + + // create the service for the testwasmlib smart contract + svc, err := testwasmlibclient.NewTestWasmLibService(svcClient, myChainID) require.NoError(t, err) - mySeed := seed.NewSeed(seedBytes) - keyPair := mySeed.KeyPair(0) - ed25519Address := ledgerstate.NewED25519Address(keyPair.PublicKey) - address := wasmclient.Address(base58.Encode(ed25519Address.Bytes())) - cl := wasmclient.DefaultServiceClient() - svc, err := testwasmlibclient.NewTestWasmLibService(cl, "qjA8Ybw4WijnmGUqDtNcPhAxFymjQKepNyyfp5BUGsWP") - require.NoError(t, err) - svc.SignRequests(keyPair) + // we'll use the first address in the seed to sign requests + svc.SignRequests(wasmclient.SeedToKeyPair(mySeed, 0)) + // get new triggerEvent interface, pass params, and post the request f := svc.TriggerEvent() f.Name("Lala") - f.Address(address) - req := f.Post() - require.NoError(t, req.Error()) - err = svc.WaitRequest(req) - require.NoError(t, err) + f.Address(wasmclient.SeedToAddress(mySeed, 0)) + req1 := f.Post() + require.NoError(t, req1.Error()) + // get new triggerEvent interface, pass params, and post the request f = svc.TriggerEvent() f.Name("Trala") - f.Address(address) - req = f.Post() - require.NoError(t, req.Error()) - err = svc.WaitRequest(req) + f.Address(wasmclient.SeedToAddress(mySeed, 1)) + req2 := f.Post() + require.NoError(t, req2.Error()) + + // just for fun wait in reverse order + err = svc.WaitRequest(req2) require.NoError(t, err) + + err = svc.WaitRequest(req1) + require.NoError(t, err) + + // give event handlers some time to complete + time.Sleep(4 * time.Second) } diff --git a/packages/vm/wasmlib/go/wasmclient/clientfunc.go b/packages/vm/wasmlib/go/wasmclient/clientfunc.go index 8917b8e8a1..7eaa7f328f 100644 --- a/packages/vm/wasmlib/go/wasmclient/clientfunc.go +++ b/packages/vm/wasmlib/go/wasmclient/clientfunc.go @@ -8,6 +8,9 @@ type ClientFunc struct { transfer map[string]uint64 } +// Post sends a request to the smart contract service +// You can wait for the request to complete by using the returned Request +// as parameter to Service.WaitRequest() func (f *ClientFunc) Post(funcHname uint32, args *Arguments) Request { keyPair := f.keyPair if keyPair == nil { @@ -16,6 +19,14 @@ func (f *ClientFunc) Post(funcHname uint32, args *Arguments) Request { return f.svc.PostRequest(funcHname, args, f.transfer, keyPair) } +// Sign optionally overrides the default keypair from the service func (f *ClientFunc) Sign(keyPair *ed25519.KeyPair) { f.keyPair = keyPair } + +// Transfer optionally indicates which tokens to transfer as part of the request +// The color string can be a base58-encoded 32-byte color, or "IOTA" +// The tokens are presumed to be available in the signing account on the chain +func (f *ClientFunc) Transfer(transfer map[string]uint64) { + f.transfer = transfer +} diff --git a/packages/vm/wasmlib/go/wasmclient/seed.go b/packages/vm/wasmlib/go/wasmclient/seed.go new file mode 100644 index 0000000000..2090051309 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/seed.go @@ -0,0 +1,34 @@ +package wasmclient + +import ( + "github.com/iotaledger/goshimmer/client/wallet/packages/seed" + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/mr-tron/base58" +) + +func ChainIsValid(chainID string) bool { + bytes, err := base58.Decode(chainID) + return err == nil && len(bytes) == 33 +} + +func SeedIsValid(mySeed string) bool { + seedBytes, err := base58.Decode(mySeed) + return err == nil && len(seedBytes) == 32 +} + +func SeedToAddress(mySeed string, index uint64) Address { + seedBytes, err := base58.Decode(mySeed) + if err != nil { + panic(err) + } + address := seed.NewSeed(seedBytes).Address(index) + return Address(base58.Encode(address.Address().Bytes())) +} + +func SeedToKeyPair(mySeed string, index uint64) *ed25519.KeyPair { + seedBytes, err := base58.Decode(mySeed) + if err != nil { + panic(err) + } + return seed.NewSeed(seedBytes).KeyPair(index) +} diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go index 52f3126267..778645d9d5 100644 --- a/packages/vm/wasmlib/go/wasmclient/service.go +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -73,7 +73,7 @@ func (s *Service) WaitRequest(req Request) error { } func (s *Service) startEventHandlers(ep string, handlers map[string]func([]string)) error { - chMsg := make(chan []string) + chMsg := make(chan []string, 20) chDone := make(chan bool) err := subscribe.Subscribe(ep, chMsg, chDone, true, "") if err != nil { From c296203a7e83d3aae059abc738ab4637ebc0659c Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 29 Dec 2021 21:56:04 -0800 Subject: [PATCH 027/120] Several fixes, first view test --- .../go/testwasmlibclient/service.go | 18 ++++--- .../wasm/testwasmlib/test/testwasmlib_test.go | 47 ++++++++++++++----- .../vm/wasmlib/go/wasmclient/clientview.go | 16 ++++++- packages/vm/wasmlib/go/wasmclient/results.go | 5 -- packages/vm/wasmlib/go/wasmclient/service.go | 15 ++++-- .../generator/goclienttemplates/service.go | 3 +- 6 files changed, 74 insertions(+), 30 deletions(-) diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go index cd3f8283bc..44566db07b 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go @@ -232,7 +232,8 @@ func (f *ArrayLengthView) Name(v string) { func (f *ArrayLengthView) Call() ArrayLengthResults { f.args.Mandatory(ArgName) - return ArrayLengthResults{res: f.ClientView.Call("arrayLength", &f.args)} + f.ClientView.Call("arrayLength", &f.args) + return ArrayLengthResults{res: f.Results()} } type ArrayLengthResults struct { @@ -261,7 +262,8 @@ func (f *ArrayValueView) Name(v string) { func (f *ArrayValueView) Call() ArrayValueResults { f.args.Mandatory(ArgIndex) f.args.Mandatory(ArgName) - return ArrayValueResults{res: f.ClientView.Call("arrayValue", &f.args)} + f.ClientView.Call("arrayValue", &f.args) + return ArrayValueResults{res: f.Results()} } type ArrayValueResults struct { @@ -290,7 +292,8 @@ func (f *BlockRecordView) RecordIndex(v int32) { func (f *BlockRecordView) Call() BlockRecordResults { f.args.Mandatory(ArgBlockIndex) f.args.Mandatory(ArgRecordIndex) - return BlockRecordResults{res: f.ClientView.Call("blockRecord", &f.args)} + f.ClientView.Call("blockRecord", &f.args) + return BlockRecordResults{res: f.Results()} } type BlockRecordResults struct { @@ -314,7 +317,8 @@ func (f *BlockRecordsView) BlockIndex(v int32) { func (f *BlockRecordsView) Call() BlockRecordsResults { f.args.Mandatory(ArgBlockIndex) - return BlockRecordsResults{res: f.ClientView.Call("blockRecords", &f.args)} + f.ClientView.Call("blockRecords", &f.args) + return BlockRecordsResults{res: f.Results()} } type BlockRecordsResults struct { @@ -332,7 +336,8 @@ type GetRandomView struct { } func (f *GetRandomView) Call() GetRandomResults { - return GetRandomResults{res: f.ClientView.Call("getRandom", nil)} + f.ClientView.Call("getRandom", nil) + return GetRandomResults{res: f.Results()} } type GetRandomResults struct { @@ -350,7 +355,8 @@ type IotaBalanceView struct { } func (f *IotaBalanceView) Call() IotaBalanceResults { - return IotaBalanceResults{res: f.ClientView.Call("iotaBalance", nil)} + f.ClientView.Call("iotaBalance", nil) + return IotaBalanceResults{res: f.Results()} } type IotaBalanceResults struct { diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_test.go index 4ac57772f1..a1e3bdf9b2 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_test.go @@ -5,7 +5,6 @@ import ( "strconv" "strings" "testing" - "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlib" @@ -310,13 +309,17 @@ func TestMultiRandom(t *testing.T) { } } -func TestClient(t *testing.T) { - // hardcoded seed and chain ID, taken from wasp-cli.json - // note that normally the chain has already been set up and - // the contract has already been deployed in some way - const mySeed = "6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv" +// hardcoded seed and chain ID, taken from wasp-cli.json +// note that normally the chain has already been set up and +// the contract has already been deployed in some way, so +// these values are usually available from elsewhere +const ( + mySeed = "6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv" + myChainID = "qjA8Ybw4WijnmGUqDtNcPhAxFymjQKepNyyfp5BUGsWP" +) + +func setupClient(t *testing.T) *testwasmlibclient.TestWasmLibService { require.True(t, wasmclient.SeedIsValid(mySeed)) - const myChainID = "qjA8Ybw4WijnmGUqDtNcPhAxFymjQKepNyyfp5BUGsWP" require.True(t, wasmclient.ChainIsValid(myChainID)) // we're testing against wasp-cluster, so defaults will do @@ -328,6 +331,11 @@ func TestClient(t *testing.T) { // we'll use the first address in the seed to sign requests svc.SignRequests(wasmclient.SeedToKeyPair(mySeed, 0)) + return svc +} + +func TestClientEvents(t *testing.T) { + svc := setupClient(t) // get new triggerEvent interface, pass params, and post the request f := svc.TriggerEvent() @@ -336,6 +344,9 @@ func TestClient(t *testing.T) { req1 := f.Post() require.NoError(t, req1.Error()) + // err := svc.WaitRequest(req1) + // require.NoError(t, err) + // get new triggerEvent interface, pass params, and post the request f = svc.TriggerEvent() f.Name("Trala") @@ -343,13 +354,25 @@ func TestClient(t *testing.T) { req2 := f.Post() require.NoError(t, req2.Error()) - // just for fun wait in reverse order - err = svc.WaitRequest(req2) + err := svc.WaitRequest(req2) require.NoError(t, err) +} + +func TestClientRandom(t *testing.T) { + svc := setupClient(t) + + // generate new random value + f := svc.Random() + req := f.Post() + require.NoError(t, req.Error()) - err = svc.WaitRequest(req1) + err := svc.WaitRequest(req) require.NoError(t, err) - // give event handlers some time to complete - time.Sleep(4 * time.Second) + // get current random value + v := svc.GetRandom() + res := v.Call() + require.NoError(t, v.Error()) + require.GreaterOrEqual(t, res.Random(), int64(0)) + fmt.Println("Random: ", res.Random()) } diff --git a/packages/vm/wasmlib/go/wasmclient/clientview.go b/packages/vm/wasmlib/go/wasmclient/clientview.go index e6e5c02983..3c6f21dcb3 100644 --- a/packages/vm/wasmlib/go/wasmclient/clientview.go +++ b/packages/vm/wasmlib/go/wasmclient/clientview.go @@ -1,9 +1,21 @@ package wasmclient +import "github.com/iotaledger/wasp/packages/kv/dict" + type ClientView struct { svc *Service + err error + res dict.Dict +} + +func (v *ClientView) Call(viewName string, args *Arguments) { + v.res, v.err = v.svc.CallView(viewName, args) +} + +func (v *ClientView) Error() error { + return v.err } -func (f *ClientView) Call(viewName string, args *Arguments) Results { - return f.svc.CallView(viewName, args) +func (v *ClientView) Results() Results { + return Results{res: v.res} } diff --git a/packages/vm/wasmlib/go/wasmclient/results.go b/packages/vm/wasmlib/go/wasmclient/results.go index c6f6df632b..4206b39b5b 100644 --- a/packages/vm/wasmlib/go/wasmclient/results.go +++ b/packages/vm/wasmlib/go/wasmclient/results.go @@ -9,14 +9,9 @@ import ( // The Results struct is used to gather all arguments for a smart // contract function call and encode it into a deterministic byte array type Results struct { - err error res dict.Dict } -func (r Results) Error() error { - return r.err -} - func (r Results) Exists(key string) bool { _, ok := r.res[kv.Key(key)] return ok diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go index 778645d9d5..6df6c2f260 100644 --- a/packages/vm/wasmlib/go/wasmclient/service.go +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -11,6 +11,7 @@ import ( "github.com/iotaledger/wasp/packages/iscp/colored" "github.com/iotaledger/wasp/packages/iscp/request" "github.com/iotaledger/wasp/packages/iscp/requestargs" + "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/subscribe" "github.com/mr-tron/base58" ) @@ -42,9 +43,12 @@ func (s *Service) AsClientView() ClientView { return ClientView{svc: s} } -func (s *Service) CallView(viewName string, args *Arguments) (ret Results) { - ret.res, ret.err = s.waspClient.CallView(s.chainID, s.scHname, viewName, args.args) - return ret +func (s *Service) CallView(viewName string, args *Arguments) (dict.Dict, error) { + arg := dict.Dict(nil) + if args != nil { + arg = args.args + } + return s.waspClient.CallView(s.chainID, s.scHname, viewName, arg) } func (s *Service) PostRequest(funcHname uint32, args *Arguments, transfer map[string]uint64, keyPair *ed25519.KeyPair) Request { @@ -52,7 +56,10 @@ func (s *Service) PostRequest(funcHname uint32, args *Arguments, transfer map[st if err != nil { return Request{err: err} } - reqArgs := requestargs.New().AddEncodeSimpleMany(args.args) + reqArgs := requestargs.New() + if args != nil { + reqArgs.AddEncodeSimpleMany(args.args) + } req := request.NewOffLedger(s.chainID, s.scHname, iscp.Hname(funcHname), reqArgs) req.WithTransfer(bal) req.Sign(keyPair) diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index 2dc5ba0734..1b0211f726 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -85,7 +85,8 @@ $#if param execWithArgs execNoArgs func (f *$FuncName$Kind) Call() $FuncName$+Results { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs - return $FuncName$+Results { res: f.ClientView.Call("$funcName", $args) } + f.ClientView.Call("$funcName", $args) + return $FuncName$+Results { res: f.Results() } } $#if result resultStruct `, From 1ed6c822ff3942ba0fa20967526f20b2a0cf48d1 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 30 Dec 2021 19:23:36 -0800 Subject: [PATCH 028/120] Update ts client code to mirror go client code --- .../ts/testwasmlibclient/service.ts | 80 +++++----- .../ts/testwasmlibclient/testwasmlib.ts | 3 - .../vm/wasmlib/go/wasmclient/arguments.go | 3 + .../vm/wasmlib/go/wasmclient/clientfunc.go | 18 ++- .../vm/wasmlib/go/wasmclient/clientview.go | 3 + packages/vm/wasmlib/go/wasmclient/event.go | 3 + packages/vm/wasmlib/go/wasmclient/request.go | 3 + packages/vm/wasmlib/go/wasmclient/results.go | 3 + packages/vm/wasmlib/go/wasmclient/seed.go | 3 + packages/vm/wasmlib/go/wasmclient/service.go | 31 ++-- .../vm/wasmlib/go/wasmclient/serviceclient.go | 3 + packages/vm/wasmlib/go/wasmclient/transfer.go | 31 ++++ packages/vm/wasmlib/go/wasmclient/types.go | 3 + .../vm/wasmlib/ts/wasmclient/arguments.ts | 8 +- .../vm/wasmlib/ts/wasmclient/clientfunc.ts | 39 +++++ .../vm/wasmlib/ts/wasmclient/clientview.ts | 24 +++ packages/vm/wasmlib/ts/wasmclient/index.ts | 3 + packages/vm/wasmlib/ts/wasmclient/service.ts | 104 +++++++------ .../vm/wasmlib/ts/wasmclient/serviceclient.ts | 141 +----------------- packages/vm/wasmlib/ts/wasmclient/transfer.ts | 16 +- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 87 +++++++++++ tools/schema/generator/emitter.go | 2 +- .../generator/goclienttemplates/service.go | 2 +- tools/schema/generator/gotemplates/consts.go | 2 +- tools/schema/generator/rstemplates/consts.go | 2 +- .../generator/tsclienttemplates/funcs.go | 3 - .../generator/tsclienttemplates/service.go | 19 ++- tools/schema/generator/tstemplates/consts.go | 2 +- 28 files changed, 363 insertions(+), 278 deletions(-) create mode 100644 packages/vm/wasmlib/go/wasmclient/transfer.go create mode 100644 packages/vm/wasmlib/ts/wasmclient/clientfunc.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/clientview.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/waspclient.ts diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts index b8726e9fac..5f1e2e0f87 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -42,37 +42,37 @@ const ResValue = "value"; ///////////////////////////// arrayClear ///////////////////////////// -export class ArrayClearFunc extends wasmclient.FuncObject { +export class ArrayClearFunc extends wasmclient.ClientFunc { args: wasmclient.Arguments = new wasmclient.Arguments(); name(v: string): void { this.args.setString(ArgName, v); } - public async post(): Promise { + public post(): wasmclient.RequestID { this.args.mandatory(ArgName); - this.svc.postRequest("arrayClear", this.args); + return super.post(0x88021821, this.args); } } ///////////////////////////// arrayCreate ///////////////////////////// -export class ArrayCreateFunc extends wasmclient.FuncObject { +export class ArrayCreateFunc extends wasmclient.ClientFunc { args: wasmclient.Arguments = new wasmclient.Arguments(); name(v: string): void { this.args.setString(ArgName, v); } - public async post(): Promise { + public post(): wasmclient.RequestID { this.args.mandatory(ArgName); - this.svc.postRequest("arrayCreate", this.args); + return super.post(0x1ed5b23b, this.args); } } ///////////////////////////// arraySet ///////////////////////////// -export class ArraySetFunc extends wasmclient.FuncObject { +export class ArraySetFunc extends wasmclient.ClientFunc { args: wasmclient.Arguments = new wasmclient.Arguments(); index(v: wasmclient.Int32): void { @@ -87,17 +87,17 @@ export class ArraySetFunc extends wasmclient.FuncObject { this.args.setString(ArgValue, v); } - public async post(): Promise { + public post(): wasmclient.RequestID { this.args.mandatory(ArgIndex); this.args.mandatory(ArgName); this.args.mandatory(ArgValue); - this.svc.postRequest("arraySet", this.args); + return super.post(0x2c4150b3, this.args); } } ///////////////////////////// paramTypes ///////////////////////////// -export class ParamTypesFunc extends wasmclient.FuncObject { +export class ParamTypesFunc extends wasmclient.ClientFunc { args: wasmclient.Arguments = new wasmclient.Arguments(); address(v: wasmclient.Address): void { @@ -176,23 +176,23 @@ export class ParamTypesFunc extends wasmclient.FuncObject { this.args.setUint8(ArgUint8, v); } - public async post(): Promise { - this.svc.postRequest("paramTypes", this.args); + public post(): wasmclient.RequestID { + return super.post(0x6921c4cd, this.args); } } ///////////////////////////// random ///////////////////////////// -export class RandomFunc extends wasmclient.FuncObject { +export class RandomFunc extends wasmclient.ClientFunc { - public async post(): Promise { - this.svc.postRequest("random", null); + public post(): wasmclient.RequestID { + return super.post(0xe86c97ca, null); } } ///////////////////////////// triggerEvent ///////////////////////////// -export class TriggerEventFunc extends wasmclient.FuncObject { +export class TriggerEventFunc extends wasmclient.ClientFunc { args: wasmclient.Arguments = new wasmclient.Arguments(); address(v: wasmclient.Address): void { @@ -203,25 +203,26 @@ export class TriggerEventFunc extends wasmclient.FuncObject { this.args.setString(ArgName, v); } - public async post(): Promise { + public post(): wasmclient.RequestID { this.args.mandatory(ArgAddress); this.args.mandatory(ArgName); - this.svc.postRequest("triggerEvent", this.args); + return super.post(0xd5438ac6, this.args); } } ///////////////////////////// arrayLength ///////////////////////////// -export class ArrayLengthView extends wasmclient.FuncObject { +export class ArrayLengthView extends wasmclient.ClientView { args: wasmclient.Arguments = new wasmclient.Arguments(); name(v: string): void { this.args.setString(ArgName, v); } - public async call(): Promise { + public call(): ArrayLengthResults { this.args.mandatory(ArgName); - return new ArrayLengthResults(this.svc.callView("arrayLength", this.args)); + super.call("arrayLength", this.args); + return new ArrayLengthResults(this.results()); } } @@ -234,7 +235,7 @@ export class ArrayLengthResults extends wasmclient.ViewResults { ///////////////////////////// arrayValue ///////////////////////////// -export class ArrayValueView extends wasmclient.FuncObject { +export class ArrayValueView extends wasmclient.ClientView { args: wasmclient.Arguments = new wasmclient.Arguments(); index(v: wasmclient.Int32): void { @@ -245,10 +246,11 @@ export class ArrayValueView extends wasmclient.FuncObject { this.args.setString(ArgName, v); } - public async call(): Promise { + public call(): ArrayValueResults { this.args.mandatory(ArgIndex); this.args.mandatory(ArgName); - return new ArrayValueResults(this.svc.callView("arrayValue", this.args)); + super.call("arrayValue", this.args); + return new ArrayValueResults(this.results()); } } @@ -261,7 +263,7 @@ export class ArrayValueResults extends wasmclient.ViewResults { ///////////////////////////// blockRecord ///////////////////////////// -export class BlockRecordView extends wasmclient.FuncObject { +export class BlockRecordView extends wasmclient.ClientView { args: wasmclient.Arguments = new wasmclient.Arguments(); blockIndex(v: wasmclient.Int32): void { @@ -272,10 +274,11 @@ export class BlockRecordView extends wasmclient.FuncObject { this.args.setInt32(ArgRecordIndex, v); } - public async call(): Promise { + public call(): BlockRecordResults { this.args.mandatory(ArgBlockIndex); this.args.mandatory(ArgRecordIndex); - return new BlockRecordResults(this.svc.callView("blockRecord", this.args)); + super.call("blockRecord", this.args); + return new BlockRecordResults(this.results()); } } @@ -288,16 +291,17 @@ export class BlockRecordResults extends wasmclient.ViewResults { ///////////////////////////// blockRecords ///////////////////////////// -export class BlockRecordsView extends wasmclient.FuncObject { +export class BlockRecordsView extends wasmclient.ClientView { args: wasmclient.Arguments = new wasmclient.Arguments(); blockIndex(v: wasmclient.Int32): void { this.args.setInt32(ArgBlockIndex, v); } - public async call(): Promise { + public call(): BlockRecordsResults { this.args.mandatory(ArgBlockIndex); - return new BlockRecordsResults(this.svc.callView("blockRecords", this.args)); + super.call("blockRecords", this.args); + return new BlockRecordsResults(this.results()); } } @@ -310,10 +314,11 @@ export class BlockRecordsResults extends wasmclient.ViewResults { ///////////////////////////// getRandom ///////////////////////////// -export class GetRandomView extends wasmclient.FuncObject { +export class GetRandomView extends wasmclient.ClientView { - public async call(): Promise { - return new GetRandomResults(this.svc.callView("getRandom", null)); + public call(): GetRandomResults { + super.call("getRandom", null); + return new GetRandomResults(this.results()); } } @@ -326,10 +331,11 @@ export class GetRandomResults extends wasmclient.ViewResults { ///////////////////////////// iotaBalance ///////////////////////////// -export class IotaBalanceView extends wasmclient.FuncObject { +export class IotaBalanceView extends wasmclient.ClientView { - public async call(): Promise { - return new IotaBalanceResults(this.svc.callView("iotaBalance", null)); + public call(): IotaBalanceResults { + super.call("iotaBalance", null); + return new IotaBalanceResults(this.results()); } } @@ -345,7 +351,7 @@ export class IotaBalanceResults extends wasmclient.ViewResults { export class TestWasmLibService extends wasmclient.Service { constructor(cl: wasmclient.ServiceClient, chainID: string) { - super(cl, chainID, "89703a45", events.eventHandlers); + super(cl, chainID, 0x89703a45, events.eventHandlers); } public arrayClear(): ArrayClearFunc { diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts index ca1b553dff..cbae805e63 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts @@ -4,8 +4,5 @@ import * as events from "./events" import * as service from "./service" -const client = new BasicClient(config); -const testWasmLibService = new service.TestWasmLibService(client, config.ChainId); - export function onTestWasmLibTest(event: events.EventTest): void { } diff --git a/packages/vm/wasmlib/go/wasmclient/arguments.go b/packages/vm/wasmlib/go/wasmclient/arguments.go index e63f5d784b..f88b0c83bc 100644 --- a/packages/vm/wasmlib/go/wasmclient/arguments.go +++ b/packages/vm/wasmlib/go/wasmclient/arguments.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import ( diff --git a/packages/vm/wasmlib/go/wasmclient/clientfunc.go b/packages/vm/wasmlib/go/wasmclient/clientfunc.go index 7eaa7f328f..71eb63d7f3 100644 --- a/packages/vm/wasmlib/go/wasmclient/clientfunc.go +++ b/packages/vm/wasmlib/go/wasmclient/clientfunc.go @@ -1,22 +1,25 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import "github.com/iotaledger/hive.go/crypto/ed25519" type ClientFunc struct { - svc *Service - keyPair *ed25519.KeyPair - transfer map[string]uint64 + svc *Service + keyPair *ed25519.KeyPair + xfer *Transfer } // Post sends a request to the smart contract service // You can wait for the request to complete by using the returned Request // as parameter to Service.WaitRequest() -func (f *ClientFunc) Post(funcHname uint32, args *Arguments) Request { +func (f *ClientFunc) Post(hFuncName uint32, args *Arguments) Request { keyPair := f.keyPair if keyPair == nil { keyPair = f.svc.keyPair } - return f.svc.PostRequest(funcHname, args, f.transfer, keyPair) + return f.svc.PostRequest(hFuncName, args, f.xfer, keyPair) } // Sign optionally overrides the default keypair from the service @@ -25,8 +28,7 @@ func (f *ClientFunc) Sign(keyPair *ed25519.KeyPair) { } // Transfer optionally indicates which tokens to transfer as part of the request -// The color string can be a base58-encoded 32-byte color, or "IOTA" // The tokens are presumed to be available in the signing account on the chain -func (f *ClientFunc) Transfer(transfer map[string]uint64) { - f.transfer = transfer +func (f *ClientFunc) Transfer(xfer *Transfer) { + f.xfer = xfer } diff --git a/packages/vm/wasmlib/go/wasmclient/clientview.go b/packages/vm/wasmlib/go/wasmclient/clientview.go index 3c6f21dcb3..bb70b27321 100644 --- a/packages/vm/wasmlib/go/wasmclient/clientview.go +++ b/packages/vm/wasmlib/go/wasmclient/clientview.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import "github.com/iotaledger/wasp/packages/kv/dict" diff --git a/packages/vm/wasmlib/go/wasmclient/event.go b/packages/vm/wasmlib/go/wasmclient/event.go index 93c7a4246b..d822989411 100644 --- a/packages/vm/wasmlib/go/wasmclient/event.go +++ b/packages/vm/wasmlib/go/wasmclient/event.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import ( diff --git a/packages/vm/wasmlib/go/wasmclient/request.go b/packages/vm/wasmlib/go/wasmclient/request.go index 15137992c4..fdbeb5349a 100644 --- a/packages/vm/wasmlib/go/wasmclient/request.go +++ b/packages/vm/wasmlib/go/wasmclient/request.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import ( diff --git a/packages/vm/wasmlib/go/wasmclient/results.go b/packages/vm/wasmlib/go/wasmclient/results.go index 4206b39b5b..04f987225a 100644 --- a/packages/vm/wasmlib/go/wasmclient/results.go +++ b/packages/vm/wasmlib/go/wasmclient/results.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import ( diff --git a/packages/vm/wasmlib/go/wasmclient/seed.go b/packages/vm/wasmlib/go/wasmclient/seed.go index 2090051309..971bc858f0 100644 --- a/packages/vm/wasmlib/go/wasmclient/seed.go +++ b/packages/vm/wasmlib/go/wasmclient/seed.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import ( diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go index 6df6c2f260..6bb886f021 100644 --- a/packages/vm/wasmlib/go/wasmclient/service.go +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import ( @@ -16,8 +19,6 @@ import ( "github.com/mr-tron/base58" ) -type Response map[string][]byte - type Service struct { chainID *iscp.ChainID keyPair *ed25519.KeyPair @@ -51,7 +52,7 @@ func (s *Service) CallView(viewName string, args *Arguments) (dict.Dict, error) return s.waspClient.CallView(s.chainID, s.scHname, viewName, arg) } -func (s *Service) PostRequest(funcHname uint32, args *Arguments, transfer map[string]uint64, keyPair *ed25519.KeyPair) Request { +func (s *Service) PostRequest(hFuncName uint32, args *Arguments, transfer *Transfer, keyPair *ed25519.KeyPair) Request { bal, err := makeBalances(transfer) if err != nil { return Request{err: err} @@ -60,7 +61,7 @@ func (s *Service) PostRequest(funcHname uint32, args *Arguments, transfer map[st if args != nil { reqArgs.AddEncodeSimpleMany(args.args) } - req := request.NewOffLedger(s.chainID, s.scHname, iscp.Hname(funcHname), reqArgs) + req := request.NewOffLedger(s.chainID, s.scHname, iscp.Hname(hFuncName), reqArgs) req.WithTransfer(bal) req.Sign(keyPair) err = s.waspClient.PostOffLedgerRequest(s.chainID, req) @@ -79,10 +80,10 @@ func (s *Service) WaitRequest(req Request) error { return s.waspClient.WaitUntilRequestProcessed(s.chainID, *req.id, 1*time.Minute) } -func (s *Service) startEventHandlers(ep string, handlers map[string]func([]string)) error { +func (s *Service) startEventHandlers(eventPort string, handlers map[string]func([]string)) error { chMsg := make(chan []string, 20) chDone := make(chan bool) - err := subscribe.Subscribe(ep, chMsg, chDone, true, "") + err := subscribe.Subscribe(eventPort, chMsg, chDone, true, "") if err != nil { return err } @@ -118,18 +119,16 @@ func Base58Encode(b []byte) string { return base58.Encode(b) } -func makeBalances(transfer map[string]uint64) (colored.Balances, error) { +func makeBalances(transfer *Transfer) (colored.Balances, error) { cb := colored.NewBalances() - for color, amount := range transfer { - if color == colored.IOTA.String() { - cb.Set(colored.IOTA, amount) - continue - } - c, err := colored.ColorFromBase58EncodedString(color) - if err != nil { - return nil, err + if transfer != nil { + for color, amount := range transfer.xfer { + c, err := colored.ColorFromBase58EncodedString(color) + if err != nil { + return nil, err + } + cb.Set(c, amount) } - cb.Set(c, amount) } return cb, nil } diff --git a/packages/vm/wasmlib/go/wasmclient/serviceclient.go b/packages/vm/wasmlib/go/wasmclient/serviceclient.go index 4dd3777850..72c817fb68 100644 --- a/packages/vm/wasmlib/go/wasmclient/serviceclient.go +++ b/packages/vm/wasmlib/go/wasmclient/serviceclient.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient import "github.com/iotaledger/wasp/client" diff --git a/packages/vm/wasmlib/go/wasmclient/transfer.go b/packages/vm/wasmlib/go/wasmclient/transfer.go new file mode 100644 index 0000000000..58d16f5e4e --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/transfer.go @@ -0,0 +1,31 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package wasmclient + +// The color string can be a base58-encoded 32-byte color, or "IOTA" + +type Transfer struct { + xfer map[string]uint64 +} + +func NewTransfer() *Transfer { + return &Transfer{xfer: make(map[string]uint64)} +} + +func TransferIotas(amount uint64) *Transfer { + return TransferTokens("IOTA", amount) +} + +func TransferTokens(color string, amount uint64) *Transfer { + transfer := NewTransfer() + transfer.Set(color, amount) + return transfer +} + +func (t *Transfer) Set(color string, amount uint64) { + if color == "IOTA" { + color = "11111111111111111111111111111111" + } + t.xfer[color] = amount +} diff --git a/packages/vm/wasmlib/go/wasmclient/types.go b/packages/vm/wasmlib/go/wasmclient/types.go index f5aad2329c..cd77b7a0ab 100644 --- a/packages/vm/wasmlib/go/wasmclient/types.go +++ b/packages/vm/wasmlib/go/wasmclient/types.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package wasmclient type ( diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 31f73ee092..553dcab5e2 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -60,11 +60,15 @@ export class Arguments { setChainID(key: string, val: wasmclient.ChainID): void { this.setBase58(key, val, wasmlib.TYPE_CHAIN_ID); } - + setHash(key: string, val: wasmclient.Hash): void { this.setBase58(key, val, wasmlib.TYPE_HASH); } - + + setHname(key: string, val: wasmclient.Hname): void { + this.setUint32(key, val); + } + setInt8(key: string, val: wasmclient.Int8): void { let bytes = Buffer.alloc(1); bytes.writeInt8(val, 0); diff --git a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts new file mode 100644 index 0000000000..d63fca3504 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts @@ -0,0 +1,39 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmclient from "./index" +import {IKeyPair} from "./crypto"; + +export class ClientFunc { + protected svc: wasmclient.Service; + private keyPair: IKeyPair; + private xfer: wasmclient.Transfer = new wasmclient.Transfer(); + + constructor(svc: wasmclient.Service) { + this.svc = svc; + } + + // Sends a request to the smart contract service + // You can wait for the request to complete by using the returned RequestID + // as parameter to Service.waitRequest() + post(hFuncName: wasmclient.Hname, args: wasmclient.Arguments | null): wasmclient.RequestID { + if (args == null) { + args = new wasmclient.Arguments(); + } + if (this.keyPair == null) { + this.keyPair = this.svc.keyPair; + } + return this.svc.postRequest(hFuncName, args, this.xfer, this.keyPair); + } + + // Optionally overrides the default keypair from the service + sign(keyPair: IKeyPair): void { + this.keyPair = keyPair; + } + + // Optionally indicates which tokens to transfer as part of the request + // The tokens are presumed to be available in the signing account on the chain + transfer(xfer: wasmclient.Transfer): void { + this.xfer = xfer; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/clientview.ts b/packages/vm/wasmlib/ts/wasmclient/clientview.ts new file mode 100644 index 0000000000..5e3fd42028 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/clientview.ts @@ -0,0 +1,24 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmclient from "./index" + +export class ClientView { + private svc: wasmclient.Service; + private res: wasmclient.Results; + + constructor(svc: wasmclient.Service) { + this.svc = svc; + } + + call(viewName: string, args: wasmclient.Arguments | null): void { + if (args == null) { + args = new wasmclient.Arguments(); + } + this.res = this.svc.callView(viewName, args); + } + + results(): wasmclient.Results { + return this.res; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/index.ts b/packages/vm/wasmlib/ts/wasmclient/index.ts index a4fc7b16d8..ac8aaeb8a0 100644 --- a/packages/vm/wasmlib/ts/wasmclient/index.ts +++ b/packages/vm/wasmlib/ts/wasmclient/index.ts @@ -2,9 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 export * from "./arguments"; +export * from "./clientfunc"; +export * from "./clientview"; export * from "./event"; export * from "./results"; export * from "./service"; export * from "./serviceclient"; export * from "./transfer"; export * from "./types"; +export * from "./waspclient"; diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index f03748ea47..72552ff7ef 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -2,21 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import * as wasmclient from "./index" -import * as wasp from "../../../../../contracts/wasm/fairroulette/frontend/src/lib/wasp_client"; -import config from './config.dev'; import {Base58, ED25519, IKeyPair} from "./crypto"; import {Buffer} from "./buffer"; +import {blake2b} from 'blakejs'; export type EventHandlers = { [key: string]: (message: string[]) => void }; -export class FuncObject { - svc: Service; - - constructor(svc: Service) { - this.svc = svc; - } -} - export class ViewResults { res: wasmclient.Results; @@ -26,85 +17,90 @@ export class ViewResults { } export class Service { - private client: wasmclient.ServiceClient; - private walletService: wasp.WalletService; + private waspClient: wasmclient.WaspClient; private webSocket: WebSocket; + private webSocketUrl: string; + public keyPair: IKeyPair; private eventHandlers: EventHandlers; - private keyPair: IKeyPair; public chainId: string; public scHname: wasmclient.Hname; - constructor(client: ServiceClient, chainId: string, scHname: wasmclient.Hname, eventHandlers: EventHandlers) { - this.client = client; + constructor(client: wasmclient.ServiceClient, chainId: string, scHname: wasmclient.Hname, eventHandlers: EventHandlers) { + this.waspClient = client.waspClient; this.chainId = chainId; this.scHname = scHname; - this.eventHandlers = eventHandlers; - this.walletService = new wasp.WalletService(client); - this.connectWebSocket(); - } - - private connectWebSocket(): void { - const webSocketUrl = config.waspWebSocketUrl.replace('%chainId', this.chainId); - // eslint-disable-next-line no-console - console.log(`Connecting to Websocket => ${webSocketUrl}`); - this.webSocket = new WebSocket(webSocketUrl); - this.webSocket.addEventListener('message', (x) => this.handleIncomingMessage(x)); - this.webSocket.addEventListener('close', () => setTimeout(this.connectWebSocket.bind(this), 1000)); - } - - private handleIncomingMessage(message: MessageEvent): void { - // expect vmmsg contract.event|parameters - const msg = message.data.toString().split(' '); - if (msg.length != 4 || msg[0] != 'vmmsg') { - return; - } - const topics = msg[3].split('|'); - const topic = topics[0]; - if (this.eventHandlers[topic] != undefined) { - this.eventHandlers[topic](msg.slice(1)); - } + this.startEventHandlers(client.eventPort, eventHandlers); } // calls a view - public async callView(viewName: string, args: wasmclient.Arguments): Promise { - const response = await this.client.callView( + public callView(viewName: string, args: wasmclient.Arguments): wasmclient.Results { + const response = this.waspClient.callView( this.chainId, this.scHname.toString(16), viewName, args, ); - const resultMap: ParameterResult = {}; + const res = new wasmclient.Results(); if (response.Items) { for (let item of response.Items) { const key = Buffer.from(item.Key, "base64").toString(); const value = Buffer.from(item.Value, "base64"); - resultMap[key] = value; + res.res.set(key, value); } } - return resultMap; + return res; } // posts off-tangle request - public async postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer?: wasmclient.Transfer): Promise { - if (!transfer) { - transfer = new wasmclient.Transfer(); - } - + public postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): wasmclient.RequestID { // get request essence ready for signing let essence = Base58.decode(this.chainId); essence.writeUInt32LE(this.scHname, essence.length); essence.writeUInt32LE(hFuncName, essence.length); - essence = Buffer.concat([essence, args.encode(), this.keyPair.publicKey]); + essence = Buffer.concat([essence, args.encode(), keyPair.publicKey]); essence.writeBigUInt64LE(BigInt(performance.now()), essence.length); essence = Buffer.concat([essence, transfer.encode()]); let buf = Buffer.alloc(0); const requestTypeOffledger = 1; buf.writeUInt8(requestTypeOffledger, 0); - buf = Buffer.concat([buf, essence, ED25519.privateSign(this.keyPair, essence)]); + buf = Buffer.concat([buf, essence, ED25519.privateSign(keyPair, essence)]); + const hash = blake2b(buf, undefined, 32); + const requestID = Buffer.concat([hash, Buffer.alloc(2)]); + + this.waspClient.sendOffLedgerRequest(this.chainId, buf); + return Base58.encode(requestID); + } - await this.client.sendOffLedgerRequest(this.chainId, request); - await this.client.sendExecutionRequest(this.chainId, wasp.OffLedger.GetRequestId(request)); + public waitRequest(req: wasmclient.RequestID): void { + this.waspClient.sendExecutionRequest(this.chainId, req); + } + + private startEventHandlers(eventPort: string, eventHandlers: EventHandlers) { + this.webSocketUrl = "ws://" + eventPort + "/chain/" + this.chainId + "/ws"; + this.eventHandlers = eventHandlers + this.connectWebSocket(); + } + + private connectWebSocket(): void { + // eslint-disable-next-line no-console + console.log(`Connecting to Websocket => ${this.webSocketUrl}`); + this.webSocket = new WebSocket(this.webSocketUrl); + this.webSocket.addEventListener('message', (x) => this.handleIncomingMessage(x)); + this.webSocket.addEventListener('close', () => setTimeout(this.connectWebSocket.bind(this), 1000)); + } + + private handleIncomingMessage(message: MessageEvent): void { + // expect vmmsg contract.event|parameters + const msg = message.data.toString().split(' '); + if (msg.length != 4 || msg[0] != 'vmmsg') { + return; + } + const topics = msg[3].split('|'); + const topic = topics[0]; + if (this.eventHandlers[topic] != undefined) { + this.eventHandlers[topic](msg.slice(1)); + } } } \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts index f389e17199..421b39e93e 100644 --- a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts @@ -2,143 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import * as wasmclient from "./index" -import {Buffer} from "./buffer"; export class ServiceClient { - private configuration: BasicClientConfiguration; + waspClient: wasmclient.WaspClient; + eventPort: string; - constructor(configuration: BasicClientConfiguration) { - this.configuration = configuration; + constructor(waspAPI: string, eventPort: string) { + this.waspClient = new wasmclient.WaspClient(waspAPI); + this.eventPort = eventPort; } - public async getAllowedManaPledge(): Promise { - return this.sendRequest( - this.configuration.GoShimmerAPIUrl, - 'get', - 'mana/allowedManaPledge', - ); - } - - public async sendFaucetRequest(faucetRequest: IFaucetRequest): Promise { - const response = await this.sendRequest( - this.configuration.GoShimmerAPIUrl, - 'post', - 'faucet', - faucetRequest, - ); - - return response; - } - - public async sendOffLedgerRequest(chainId: string, offLedgerRequest: Buffer): Promise { - const request = { Request: offLedgerRequest.toString('base64') }; - - await this.sendRequestExt( - this.configuration.WaspAPIUrl, - 'post', - `request/${chainId}`, - request, - ); - } - - public async sendExecutionRequest(chainId: string, offLedgerRequestId: string): Promise { - await this.sendRequestExt( - this.configuration.WaspAPIUrl, - 'get', - `chain/${chainId}/request/${offLedgerRequestId}/wait`, - ); - } - - public async callView(chainId: string, contractHName: string, entryPoint: string): Promise { - const url = `chain/${chainId}/contract/${contractHName}/callview/${entryPoint}`; - - const result = await this.sendRequestExt(this.configuration.WaspAPIUrl, 'get', url); - - return result.body; - } - - public async getAddressUnspentOutputs(address: string): Promise { - return this.sendRequest( - this.configuration.GoShimmerAPIUrl, - 'get', - `ledgerstate/addresses/${address}/unspentOutputs`, - ); - } - - public async unspentOutputs(request: IUnspentOutputsRequest): Promise { - return this.sendRequest( - this.configuration.GoShimmerAPIUrl, - 'post', - 'ledgerstate/addresses/unspentOutputs', - request, - ); - } - - public async sendTransaction(request: ISendTransactionRequest): Promise { - return this.sendRequest( - this.configuration.GoShimmerAPIUrl, - 'post', - 'ledgerstate/transactions', - request, - ); - } - - private async sendRequest( - url: string, - verb: 'put' | 'post' | 'get' | 'delete', - path: string, - request?: T | undefined, - ): Promise { - const response = await this.sendRequestExt(url, verb, path, request); - - return response.body; - } - - private async sendRequestExt( - url: string, - verb: 'put' | 'post' | 'get' | 'delete', - path: string, - request?: T | undefined, - ): Promise> { - let response: U; - let fetchResponse: Response; - - try { - const headers: { [id: string]: string } = { - 'Content-Type': 'application/json', - }; - - if (verb == 'get' || verb == 'delete') { - fetchResponse = await fetch(`${url}/${path}`, { - method: verb, - headers, - }); - } else if (verb == 'post' || verb == 'put') { - fetchResponse = await fetch(`${url}/${path}`, { - method: verb, - headers, - body: JSON.stringify(request), - }); - } - - if (!fetchResponse) { - throw new Error('No data was returned from the API'); - } - - try { - response = await fetchResponse.json(); - } catch (err) { - if (!fetchResponse.ok) { - const text = await fetchResponse.text(); - throw new Error(err.message + ' --- ' + text); - } - } - } catch (err) { - throw new Error( - `The application is not able to complete the request, due to the following error:\n\n${err.message}`, - ); - } - - return { body: response, response: fetchResponse }; + static default(): ServiceClient { + //TODO use TCP instead of websocket for event listener? + return new ServiceClient("127.0.0.1:9090", "127.0.0.1:9090"); // "127.0.0.1:5550"); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/transfer.ts b/packages/vm/wasmlib/ts/wasmclient/transfer.ts index 01d14dc48b..02ebc9883a 100644 --- a/packages/vm/wasmlib/ts/wasmclient/transfer.ts +++ b/packages/vm/wasmlib/ts/wasmclient/transfer.ts @@ -9,17 +9,19 @@ export class Transfer { private xfer = new Map(); static iotas(amount: wasmclient.Uint64): Transfer { - const iotaColorBase58 = "11111111111111111111111111111111"; - return Transfer.tokens(iotaColorBase58, amount); + return Transfer.tokens("IOTA", amount); } static tokens(color: string, amount: wasmclient.Uint64): Transfer { - let transfers = new Transfer(); - transfers.set(color, amount); - return transfers; + let transfer = new Transfer(); + transfer.set(color, amount); + return transfer; } set(color: string, amount: wasmclient.Uint64) { + if (color == "IOTA") { + color = "11111111111111111111111111111111" + } this.xfer.set(Base58.decode(color), amount); } @@ -31,7 +33,7 @@ export class Transfer { // and then the 8-byte amount. encode(): wasmclient.Bytes { let keys = new Array(); - for (const [key,val] of this.xfer) { + for (const [key, val] of this.xfer) { // filter out zero transfers if (val != BigInt(0)) { keys.push(key); @@ -47,4 +49,4 @@ export class Transfer { } return buf; } -} \ No newline at end of file +} diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts new file mode 100644 index 0000000000..9694f32f8d --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -0,0 +1,87 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmclient from "./index" +import {Buffer} from "./buffer"; + +export class WaspClient { + private waspAPI: string; + + constructor(waspAPI: string) { + this.waspAPI = "http://" + waspAPI; + } + + public async sendOffLedgerRequest(chainId: string, offLedgerRequest: Buffer): Promise { + const request = { Request: offLedgerRequest.toString('base64') }; + await this.sendRequestExt( + this.waspAPI, + 'post', + `request/${chainId}`, + request, + ); + } + + public async sendExecutionRequest(chainId: string, offLedgerRequestId: string): Promise { + await this.sendRequestExt( + this.waspAPI, + 'get', + `chain/${chainId}/request/${offLedgerRequestId}/wait`, + ); + } + + public async callView(chainId: string, contractHName: string, entryPoint: string, args: wasmclient.Arguments): Promise { + const url = `chain/${chainId}/contract/${contractHName}/callview/${entryPoint}`; + + const result = await this.sendRequestExt(this.waspAPI, 'get', url); + + return result.body; + } + + private async sendRequestExt( + url: string, + verb: 'put' | 'post' | 'get' | 'delete', + path: string, + request?: T | undefined, + ): Promise> { + let response: U; + let fetchResponse: Response; + + try { + const headers: { [id: string]: string } = { + 'Content-Type': 'application/json', + }; + + if (verb == 'get' || verb == 'delete') { + fetchResponse = await fetch(`${url}/${path}`, { + method: verb, + headers, + }); + } else if (verb == 'post' || verb == 'put') { + fetchResponse = await fetch(`${url}/${path}`, { + method: verb, + headers, + body: JSON.stringify(request), + }); + } + + if (!fetchResponse) { + throw new Error('No data was returned from the API'); + } + + try { + response = await fetchResponse.json(); + } catch (err) { + if (!fetchResponse.ok) { + const text = await fetchResponse.text(); + throw new Error(err.message + ' --- ' + text); + } + } + } catch (err) { + throw new Error( + `The application is not able to complete the request, due to the following error:\n\n${err.message}`, + ); + } + + return { body: response, response: fetchResponse }; + } +} diff --git a/tools/schema/generator/emitter.go b/tools/schema/generator/emitter.go index e53e8f3747..85310d21e5 100644 --- a/tools/schema/generator/emitter.go +++ b/tools/schema/generator/emitter.go @@ -438,7 +438,7 @@ func (g *GenBase) setFieldKeys(pad bool, maxCamelLength, maxSnakeLength int) { func (g *GenBase) setFuncKeys(pad bool, maxCamelLength, maxSnakeLength int) { g.setMultiKeyValues("funcName", g.currentFunc.Name) g.setMultiKeyValues("kind", g.currentFunc.Kind) - g.keys["funcHname"] = iscp.Hn(g.keys["funcName"]).String() + g.keys["hFuncName"] = iscp.Hn(g.keys["funcName"]).String() grant := g.currentFunc.Access comment := "" index := strings.Index(grant, "//") diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index 1b0211f726..a35270a4c9 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -76,7 +76,7 @@ func (f *$FuncName$Kind) $FldName(a []$fldLangType) { func (f *$FuncName$Kind) Post() wasmclient.Request { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs - return f.ClientFunc.Post(0x$funcHname, $args) + return f.ClientFunc.Post(0x$hFuncName, $args) } `, // ******************************* diff --git a/tools/schema/generator/gotemplates/consts.go b/tools/schema/generator/gotemplates/consts.go index ac7d6e350c..81474d2c23 100644 --- a/tools/schema/generator/gotemplates/consts.go +++ b/tools/schema/generator/gotemplates/consts.go @@ -56,6 +56,6 @@ $#each state constField `, // ******************************* "constHFunc": ` - H$Kind$FuncName$funcPad = wasmlib.ScHname(0x$funcHname) + H$Kind$FuncName$funcPad = wasmlib.ScHname(0x$hFuncName) `, } diff --git a/tools/schema/generator/rstemplates/consts.go b/tools/schema/generator/rstemplates/consts.go index 4c3a5bcc4b..95913a1a8d 100644 --- a/tools/schema/generator/rstemplates/consts.go +++ b/tools/schema/generator/rstemplates/consts.go @@ -46,6 +46,6 @@ pub$crate const $KIND$+_$FUNC_NAME$func_pad : &str = "$funcName"; `, // ******************************* "constHFunc": ` -pub$crate const H$KIND$+_$FUNC_NAME$func_pad : ScHname = ScHname(0x$funcHname); +pub$crate const H$KIND$+_$FUNC_NAME$func_pad : ScHname = ScHname(0x$hFuncName); `, } diff --git a/tools/schema/generator/tsclienttemplates/funcs.go b/tools/schema/generator/tsclienttemplates/funcs.go index 825912c938..37f15b7fab 100644 --- a/tools/schema/generator/tsclienttemplates/funcs.go +++ b/tools/schema/generator/tsclienttemplates/funcs.go @@ -5,9 +5,6 @@ var funcsTs = map[string]string{ "funcs.ts": ` import * as events from "./events" import * as service from "./service" - -const client = new BasicClient(config); -const $pkgName$+Service = new service.$PkgName$+Service(client, config.ChainId); $#each events funcSignature `, // ******************************* diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 947bad07c1..c6f47ba0e7 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -16,7 +16,7 @@ $#each func funcStruct export class $PkgName$+Service extends wasmclient.Service { constructor(cl: wasmclient.ServiceClient, chainID: string) { - super(cl, chainID, "$hscName", events.eventHandlers); + super(cl, chainID, 0x$hscName, events.eventHandlers); } $#each func serviceFunction } @@ -34,7 +34,7 @@ const Res$FldName = "$fldAlias"; ///////////////////////////// $funcName ///////////////////////////// -export class $FuncName$Kind extends wasmclient.FuncObject { +export class $FuncName$Kind extends wasmclient.Client$Kind { $#if param funcArgsMember $#each param funcArgSetter $#if func funcPost viewCall @@ -69,21 +69,20 @@ $#if array funcArgSetterArray funcArgSetterBasic // ******************************* "funcPost": ` - public async post(): Promise { + public post(): wasmclient.RequestID { $#each mandatory mandatoryCheck -$#set exec this.svc.postRequest $#if param execWithArgs execNoArgs - $exec; + return super.post(0x$hFuncName, $args); } `, // ******************************* "viewCall": ` - public async call(): Promise<$FuncName$+Results> { + public call(): $FuncName$+Results { $#each mandatory mandatoryCheck -$#set exec this.svc.callView $#if param execWithArgs execNoArgs - return new $FuncName$+Results($exec); + super.call("$funcName", $args); + return new $FuncName$+Results(this.results()); } `, // ******************************* @@ -92,11 +91,11 @@ $#if param execWithArgs execNoArgs `, // ******************************* "execWithArgs": ` -$#set exec $exec("$funcName", this.args) +$#set args this.args `, // ******************************* "execNoArgs": ` -$#set exec $exec("$funcName", null) +$#set args null `, // ******************************* "resultStruct": ` diff --git a/tools/schema/generator/tstemplates/consts.go b/tools/schema/generator/tstemplates/consts.go index 002eade14c..5f56e70bf0 100644 --- a/tools/schema/generator/tstemplates/consts.go +++ b/tools/schema/generator/tstemplates/consts.go @@ -44,6 +44,6 @@ export const $Kind$FuncName$funcPad = "$funcName"; `, // ******************************* "constHFunc": ` -export const H$Kind$FuncName$funcPad = new wasmlib.ScHname(0x$funcHname); +export const H$Kind$FuncName$funcPad = new wasmlib.ScHname(0x$hFuncName); `, } From f9f7fe4a470a5950c2c278c14eefb1c8956355a6 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Fri, 31 Dec 2021 19:47:03 -0800 Subject: [PATCH 029/120] Initial ts wasmclient library --- .../wasm/fairroulette/frontend/package.json | 1 - .../ts/testwasmlibclient/service.ts | 54 +++--- .../vm/wasmlib/ts/wasmclient/clientfunc.ts | 8 +- .../vm/wasmlib/ts/wasmclient/clientview.ts | 9 +- .../vm/wasmlib/ts/wasmclient/crypto/hash.ts | 8 + .../vm/wasmlib/ts/wasmclient/crypto/hname.ts | 25 --- .../vm/wasmlib/ts/wasmclient/crypto/index.ts | 8 +- .../vm/wasmlib/ts/wasmclient/crypto/seed.ts | 158 +++++++++--------- .../wasmlib/ts/wasmclient/package-lock.json | 38 +++++ .../vm/wasmlib/ts/wasmclient/package.json | 14 ++ packages/vm/wasmlib/ts/wasmclient/results.ts | 8 + packages/vm/wasmlib/ts/wasmclient/service.ts | 37 +--- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 90 ++++++---- .../vm/wasmlib/ts/wasmlib/package-lock.json | 58 +++++++ packages/vm/wasmlib/ts/wasmlib/package.json | 2 +- .../generator/tsclienttemplates/service.go | 9 +- 16 files changed, 308 insertions(+), 219 deletions(-) create mode 100644 packages/vm/wasmlib/ts/wasmclient/crypto/hash.ts delete mode 100644 packages/vm/wasmlib/ts/wasmclient/crypto/hname.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/package-lock.json create mode 100644 packages/vm/wasmlib/ts/wasmclient/package.json create mode 100644 packages/vm/wasmlib/ts/wasmlib/package-lock.json diff --git a/contracts/wasm/fairroulette/frontend/package.json b/contracts/wasm/fairroulette/frontend/package.json index db687958bd..3ea890b88c 100644 --- a/contracts/wasm/fairroulette/frontend/package.json +++ b/contracts/wasm/fairroulette/frontend/package.json @@ -42,7 +42,6 @@ "svelte-check": "^2.0.0", "svelte-preprocess": "^4.0.0", "threads": "^1.6.5", - "tslib": "^2.0.0", "tweetnacl": "^1.0.3", "typescript": "^4.0.0", "uuid": "^8.3.2" diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts index 5f1e2e0f87..306bdcdf68 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -49,9 +49,9 @@ export class ArrayClearFunc extends wasmclient.ClientFunc { this.args.setString(ArgName, v); } - public post(): wasmclient.RequestID { + public async post(): Promise { this.args.mandatory(ArgName); - return super.post(0x88021821, this.args); + return await super.post(0x88021821, this.args); } } @@ -64,9 +64,9 @@ export class ArrayCreateFunc extends wasmclient.ClientFunc { this.args.setString(ArgName, v); } - public post(): wasmclient.RequestID { + public async post(): Promise { this.args.mandatory(ArgName); - return super.post(0x1ed5b23b, this.args); + return await super.post(0x1ed5b23b, this.args); } } @@ -87,11 +87,11 @@ export class ArraySetFunc extends wasmclient.ClientFunc { this.args.setString(ArgValue, v); } - public post(): wasmclient.RequestID { + public async post(): Promise { this.args.mandatory(ArgIndex); this.args.mandatory(ArgName); this.args.mandatory(ArgValue); - return super.post(0x2c4150b3, this.args); + return await super.post(0x2c4150b3, this.args); } } @@ -176,8 +176,8 @@ export class ParamTypesFunc extends wasmclient.ClientFunc { this.args.setUint8(ArgUint8, v); } - public post(): wasmclient.RequestID { - return super.post(0x6921c4cd, this.args); + public async post(): Promise { + return await super.post(0x6921c4cd, this.args); } } @@ -185,8 +185,8 @@ export class ParamTypesFunc extends wasmclient.ClientFunc { export class RandomFunc extends wasmclient.ClientFunc { - public post(): wasmclient.RequestID { - return super.post(0xe86c97ca, null); + public async post(): Promise { + return await super.post(0xe86c97ca, null); } } @@ -203,10 +203,10 @@ export class TriggerEventFunc extends wasmclient.ClientFunc { this.args.setString(ArgName, v); } - public post(): wasmclient.RequestID { + public async post(): Promise { this.args.mandatory(ArgAddress); this.args.mandatory(ArgName); - return super.post(0xd5438ac6, this.args); + return await super.post(0xd5438ac6, this.args); } } @@ -219,10 +219,9 @@ export class ArrayLengthView extends wasmclient.ClientView { this.args.setString(ArgName, v); } - public call(): ArrayLengthResults { + public async call(): Promise { this.args.mandatory(ArgName); - super.call("arrayLength", this.args); - return new ArrayLengthResults(this.results()); + return new ArrayLengthResults(await this.callView("arrayLength", this.args)); } } @@ -246,11 +245,10 @@ export class ArrayValueView extends wasmclient.ClientView { this.args.setString(ArgName, v); } - public call(): ArrayValueResults { + public async call(): Promise { this.args.mandatory(ArgIndex); this.args.mandatory(ArgName); - super.call("arrayValue", this.args); - return new ArrayValueResults(this.results()); + return new ArrayValueResults(await this.callView("arrayValue", this.args)); } } @@ -274,11 +272,10 @@ export class BlockRecordView extends wasmclient.ClientView { this.args.setInt32(ArgRecordIndex, v); } - public call(): BlockRecordResults { + public async call(): Promise { this.args.mandatory(ArgBlockIndex); this.args.mandatory(ArgRecordIndex); - super.call("blockRecord", this.args); - return new BlockRecordResults(this.results()); + return new BlockRecordResults(await this.callView("blockRecord", this.args)); } } @@ -298,10 +295,9 @@ export class BlockRecordsView extends wasmclient.ClientView { this.args.setInt32(ArgBlockIndex, v); } - public call(): BlockRecordsResults { + public async call(): Promise { this.args.mandatory(ArgBlockIndex); - super.call("blockRecords", this.args); - return new BlockRecordsResults(this.results()); + return new BlockRecordsResults(await this.callView("blockRecords", this.args)); } } @@ -316,9 +312,8 @@ export class BlockRecordsResults extends wasmclient.ViewResults { export class GetRandomView extends wasmclient.ClientView { - public call(): GetRandomResults { - super.call("getRandom", null); - return new GetRandomResults(this.results()); + public async call(): Promise { + return new GetRandomResults(await this.callView("getRandom", null)); } } @@ -333,9 +328,8 @@ export class GetRandomResults extends wasmclient.ViewResults { export class IotaBalanceView extends wasmclient.ClientView { - public call(): IotaBalanceResults { - super.call("iotaBalance", null); - return new IotaBalanceResults(this.results()); + public async call(): Promise { + return new IotaBalanceResults(await this.callView("iotaBalance", null)); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts index d63fca3504..24bca456fb 100644 --- a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts +++ b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts @@ -16,24 +16,24 @@ export class ClientFunc { // Sends a request to the smart contract service // You can wait for the request to complete by using the returned RequestID // as parameter to Service.waitRequest() - post(hFuncName: wasmclient.Hname, args: wasmclient.Arguments | null): wasmclient.RequestID { + public async post(hFuncName: wasmclient.Hname, args: wasmclient.Arguments | null): Promise { if (args == null) { args = new wasmclient.Arguments(); } if (this.keyPair == null) { this.keyPair = this.svc.keyPair; } - return this.svc.postRequest(hFuncName, args, this.xfer, this.keyPair); + return await this.svc.postRequest(hFuncName, args, this.xfer, this.keyPair); } // Optionally overrides the default keypair from the service - sign(keyPair: IKeyPair): void { + public sign(keyPair: IKeyPair): void { this.keyPair = keyPair; } // Optionally indicates which tokens to transfer as part of the request // The tokens are presumed to be available in the signing account on the chain - transfer(xfer: wasmclient.Transfer): void { + public transfer(xfer: wasmclient.Transfer): void { this.xfer = xfer; } } diff --git a/packages/vm/wasmlib/ts/wasmclient/clientview.ts b/packages/vm/wasmlib/ts/wasmclient/clientview.ts index 5e3fd42028..8fbde5939b 100644 --- a/packages/vm/wasmlib/ts/wasmclient/clientview.ts +++ b/packages/vm/wasmlib/ts/wasmclient/clientview.ts @@ -5,20 +5,15 @@ import * as wasmclient from "./index" export class ClientView { private svc: wasmclient.Service; - private res: wasmclient.Results; constructor(svc: wasmclient.Service) { this.svc = svc; } - call(viewName: string, args: wasmclient.Arguments | null): void { + protected async callView(viewName: string, args: wasmclient.Arguments | null): Promise { if (args == null) { args = new wasmclient.Arguments(); } - this.res = this.svc.callView(viewName, args); - } - - results(): wasmclient.Results { - return this.res; + return await this.svc.callView(viewName, args); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/hash.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/hash.ts new file mode 100644 index 0000000000..078e92b2b1 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/hash.ts @@ -0,0 +1,8 @@ +import {blake2b} from 'blakejs'; +import {Buffer} from '../buffer'; + +export class Hash { + public static from(bytes: Buffer): Buffer { + return blake2b(bytes, undefined, 32 /* Blake256 */); + } +} \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/hname.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/hname.ts deleted file mode 100644 index dee4a6d40e..0000000000 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/hname.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { blake2b } from 'blakejs'; -import { Buffer } from '../buffer'; - -export class HName { - public static HashAsString(textToHash: string): string { - const hashNumber = this.HashAsNumber(textToHash); - const hashString = hashNumber.toString(16); - - return hashString; - } - - public static HashAsBuffer(textToHash: string): Buffer { - const buffer = Buffer.from(textToHash); - const result = blake2b(buffer, undefined, 32); - - return Buffer.from(result); - } - - public static HashAsNumber(textToHash: string): number { - const bufferedHash = this.HashAsBuffer(textToHash); - const resultHash = Buffer.from(bufferedHash).readUInt32LE(0); - - return resultHash; - } -} diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/index.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/index.ts index 21203dfba2..1a3d9fa59e 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/index.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/index.ts @@ -1,6 +1,6 @@ // @index('./*.ts', f => `export * from '${f.path}'`) -export * from './base58'; -export * from './ed25519'; -export * from './hname'; -export * from './seed'; +export * from "./base58"; +export * from "./ed25519"; +export * from "./hash"; +export * from "./seed"; // @endindex diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts index dafbabbfb4..0c2330fc9b 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts @@ -1,96 +1,94 @@ -import { Base58 } from './base58'; -import { blake2b } from 'blakejs'; -import { Buffer } from '../buffer'; -import { ED25519, IKeyPair } from './ed25519'; +import {Base58} from './base58'; +import {Buffer} from '../buffer'; +import {ED25519, IKeyPair} from './ed25519'; +import {Hash} from "./hash"; export class Seed { - /** - * SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. - */ - public static SEED_SIZE: number = 32; - /** - * Generate a new seed. - * @returns The random seed. - */ - public static generate(): Buffer { - const cryptoObj: Crypto = window.crypto; - const array = new Uint32Array(Seed.SEED_SIZE); + /** + * SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. + */ + public static SEED_SIZE: number = 32; - cryptoObj.getRandomValues(array); + /** + * Generate a new seed. + * @returns The random seed. + */ + public static generate(): Buffer { + const cryptoObj: Crypto = window.crypto; + const array = new Uint32Array(Seed.SEED_SIZE); - return Buffer.from(array); - } + cryptoObj.getRandomValues(array); - /** - * Generate the nth subseed. - * @param seed The seed to generate from. - * @param index The index of the subseed to generate. - * @returns The subseed. - */ - public static subseed(seed: Buffer, index: number): Buffer { - const indexBytes = Buffer.alloc(8); - indexBytes.writeBigUInt64LE(BigInt(index), 0); - - const hashOfPaddedBuffer = blake2b(indexBytes, undefined, 32 /* Blake256 */); - - const subseed = Buffer.alloc(this.SEED_SIZE); - Seed.xorBytes(seed, Buffer.from(hashOfPaddedBuffer), subseed); - return subseed; - } + return Buffer.from(array); + } - /** - * Is the string a valid seed. - * @param seed The seed to check. - * @returns True is the seed is valid. - */ - public static isValid(seed?: string): boolean { - if (!seed) { - return false; + /** + * Generate the nth subseed. + * @param seed The seed to generate from. + * @param index The index of the subseed to generate. + * @returns The subseed. + */ + public static subseed(seed: Buffer, index: number): Buffer { + const indexBytes = Buffer.alloc(8); + indexBytes.writeBigUInt64LE(BigInt(index), 0); + const subseed = Buffer.alloc(this.SEED_SIZE); + Seed.xorBytes(seed, Hash.from(indexBytes), subseed); + return subseed; } - if (!Base58.isValid(seed)) { - return false; + + /** + * Is the string a valid seed. + * @param seed The seed to check. + * @returns True is the seed is valid. + */ + public static isValid(seed?: string): boolean { + if (!seed) { + return false; + } + if (!Base58.isValid(seed)) { + return false; + } + return Base58.decode(seed).length == Seed.SEED_SIZE; } - return Base58.decode(seed).length == Seed.SEED_SIZE; - } - /** - * Generate a key pair for the seed index. - * @param seed The seed. - * @param index The index of the address to generate. - * @returns The generated address key pair. - */ - public static generateKeyPair(seed: Buffer, index: number): IKeyPair { - const subSeed = Seed.subseed(seed, index); - return ED25519.keyPairFromSeed(subSeed); - } + /** + * Generate a key pair for the seed index. + * @param seed The seed. + * @param index The index of the address to generate. + * @returns The generated address key pair. + */ + public static generateKeyPair(seed: Buffer, index: number): IKeyPair { + const subSeed = Seed.subseed(seed, index); + return ED25519.keyPairFromSeed(subSeed); + } - /** - * Generate an address for the seed. - * @param seed The seed. - * @param index The index of the address to generate. - * @returns The generated address. - */ - public static generateAddress(seed: Buffer, index: number): string { - const { publicKey } = Seed.generateKeyPair(seed, index); + /** + * Generate an address for the seed. + * @param seed The seed. + * @param index The index of the address to generate. + * @returns The generated address. + */ + public static generateAddress(seed: Buffer, index: number): string { + const {publicKey} = Seed.generateKeyPair(seed, index); - const digest = blake2b(publicKey, undefined, 32); + const digest = Hash.from(publicKey); - const buffer = Buffer.alloc(Seed.SEED_SIZE + 1); - buffer[0] = ED25519.VERSION; - Buffer.from(digest).copy(buffer, 1); + const buffer = Buffer.alloc(Seed.SEED_SIZE + 1); + buffer[0] = ED25519.VERSION; + digest.copy(buffer, 1); - return Base58.encode(buffer); - } + return Base58.encode(buffer); + } - /** - * XOR the two input buffers into the output. - * @param srcA The first source buffer. - * @param srcB The second source buffer, - * @param dest The destination buffer. - */ - private static xorBytes(srcA: Buffer, srcB: Buffer, dest: Buffer): void { - for (let i = 0; i < srcA.byteLength; i++) { - dest.writeUInt8(srcA.readUInt8(i) ^ srcB.readUInt8(i), i); + /** + * XOR the two input buffers into the output. + * @param srcA The first source buffer. + * @param srcB The second source buffer, + * @param dest The destination buffer. + */ + private static xorBytes(srcA: Buffer, srcB: Buffer, dest: Buffer): void { + for (let i = 0; i < srcA.byteLength; i++) { + dest.writeUInt8(srcA.readUInt8(i) ^ srcB.readUInt8(i), i); + } } - } } diff --git a/packages/vm/wasmlib/ts/wasmclient/package-lock.json b/packages/vm/wasmlib/ts/wasmclient/package-lock.json new file mode 100644 index 0000000000..b091c0f6b9 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/package-lock.json @@ -0,0 +1,38 @@ +{ + "name": "wasmclient", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "blakejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==", + "dev": true + }, + "nanoevents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/nanoevents/-/nanoevents-6.0.2.tgz", + "integrity": "sha512-FRS2otuFcPPYDPYViNWQ42+1iZqbXydinkRHTHFxrF4a1CpBfmydR9zkI44WSXAXCyPrkcGtPk5CnpW6Y3lFKQ==", + "dev": true + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true + } + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/package.json b/packages/vm/wasmlib/ts/wasmclient/package.json new file mode 100644 index 0000000000..f88b497c51 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/package.json @@ -0,0 +1,14 @@ +{ + "name": "wasmclient", + "description": "WasmClient, client interface library for ISCP Wasm VM", + "version": "1.0.0", + "author": "Eric Hop", + "dependencies": {}, + "devDependencies": { + "base64-js": "^1.5.1", + "blakejs": "^1.1.1", + "nanoevents": "^6.0.0", + "tslib": "^2.0.0", + "tweetnacl": "^1.0.3" + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/results.ts b/packages/vm/wasmlib/ts/wasmclient/results.ts index 7377540430..edba99ebbf 100644 --- a/packages/vm/wasmlib/ts/wasmclient/results.ts +++ b/packages/vm/wasmlib/ts/wasmclient/results.ts @@ -5,6 +5,14 @@ import * as wasmlib from "../wasmlib"; import {Base58} from "./crypto"; import {Buffer} from "./buffer"; +export class ViewResults { + res: wasmclient.Results; + + constructor(res: wasmclient.Results) { + this.res = res; + } +} + export class Results { res = new Map(); diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 72552ff7ef..4c22d19c85 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -2,20 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import * as wasmclient from "./index" -import {Base58, ED25519, IKeyPair} from "./crypto"; +import {Base58, ED25519, IKeyPair, Hash} from "./crypto"; import {Buffer} from "./buffer"; -import {blake2b} from 'blakejs'; export type EventHandlers = { [key: string]: (message: string[]) => void }; -export class ViewResults { - res: wasmclient.Results; - - constructor(res: wasmclient.Results) { - this.res = res; - } -} - export class Service { private waspClient: wasmclient.WaspClient; private webSocket: WebSocket; @@ -32,28 +23,16 @@ export class Service { this.startEventHandlers(client.eventPort, eventHandlers); } - // calls a view - public callView(viewName: string, args: wasmclient.Arguments): wasmclient.Results { - const response = this.waspClient.callView( + public async callView(viewName: string, args: wasmclient.Arguments): Promise { + return await this.waspClient.callView( this.chainId, this.scHname.toString(16), viewName, args, ); - - const res = new wasmclient.Results(); - if (response.Items) { - for (let item of response.Items) { - const key = Buffer.from(item.Key, "base64").toString(); - const value = Buffer.from(item.Value, "base64"); - res.res.set(key, value); - } - } - return res; } - // posts off-tangle request - public postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): wasmclient.RequestID { + public async postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): Promise { // get request essence ready for signing let essence = Base58.decode(this.chainId); essence.writeUInt32LE(this.scHname, essence.length); @@ -66,15 +45,15 @@ export class Service { const requestTypeOffledger = 1; buf.writeUInt8(requestTypeOffledger, 0); buf = Buffer.concat([buf, essence, ED25519.privateSign(keyPair, essence)]); - const hash = blake2b(buf, undefined, 32); + const hash = Hash.from(buf); const requestID = Buffer.concat([hash, Buffer.alloc(2)]); - this.waspClient.sendOffLedgerRequest(this.chainId, buf); + await this.waspClient.postOffLedgerRequest(this.chainId, buf); return Base58.encode(requestID); } - public waitRequest(req: wasmclient.RequestID): void { - this.waspClient.sendExecutionRequest(this.chainId, req); + public async waitRequest(reqID: wasmclient.RequestID): Promise { + await this.waspClient.waitRequest(this.chainId, reqID); } private startEventHandlers(eventPort: string, eventHandlers: EventHandlers) { diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 9694f32f8d..470bd3a0ae 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -4,6 +4,27 @@ import * as wasmclient from "./index" import {Buffer} from "./buffer"; +const headers: { [id: string]: string } = { + "Content-Type": "application/json", +}; + +interface IResponse { + error?: string; +} + +interface IExtendedResponse { + body: U; + response: Response; +} + +interface ICallViewResponse extends IResponse { + Items: [{ Key: string; Value: string }]; +} + +interface IOffLedgerRequest { + Request: string; +} + export class WaspClient { private waspAPI: string; @@ -11,35 +32,41 @@ export class WaspClient { this.waspAPI = "http://" + waspAPI; } - public async sendOffLedgerRequest(chainId: string, offLedgerRequest: Buffer): Promise { - const request = { Request: offLedgerRequest.toString('base64') }; - await this.sendRequestExt( - this.waspAPI, - 'post', - `request/${chainId}`, - request, - ); + //TODO args + public async callView(chainID: string, contractHName: string, entryPoint: string, args: wasmclient.Arguments): Promise { + const result = await this.sendRequest( + "get", + "/chain/" + chainID + "/contract/ " + contractHName + "/callview/" + entryPoint, + ); + const res = new wasmclient.Results(); + if (result.body.Items) { + for (let item of result.body.Items) { + const key = Buffer.from(item.Key, "base64").toString(); + const value = Buffer.from(item.Value, "base64"); + res.res.set(key, value); + } + } + return res; } - public async sendExecutionRequest(chainId: string, offLedgerRequestId: string): Promise { - await this.sendRequestExt( - this.waspAPI, - 'get', - `chain/${chainId}/request/${offLedgerRequestId}/wait`, + public async postOffLedgerRequest(chainID: string, offLedgerRequest: Buffer): Promise { + const request = {Request: offLedgerRequest.toString("base64")}; + await this.sendRequest( + "post", + "/request/" + chainID, + request, ); } - public async callView(chainId: string, contractHName: string, entryPoint: string, args: wasmclient.Arguments): Promise { - const url = `chain/${chainId}/contract/${contractHName}/callview/${entryPoint}`; - - const result = await this.sendRequestExt(this.waspAPI, 'get', url); - - return result.body; + public async waitRequest(chainID: string, reqID: wasmclient.RequestID): Promise { + await this.sendRequest( + "get", + "/chain/" + chainID + "/request/" + reqID + "/wait", + ); } - private async sendRequestExt( - url: string, - verb: 'put' | 'post' | 'get' | 'delete', + private async sendRequest( + verb: "put" | "post" | "get" | "delete", path: string, request?: T | undefined, ): Promise> { @@ -47,17 +74,14 @@ export class WaspClient { let fetchResponse: Response; try { - const headers: { [id: string]: string } = { - 'Content-Type': 'application/json', - }; - - if (verb == 'get' || verb == 'delete') { - fetchResponse = await fetch(`${url}/${path}`, { + const url = this.waspAPI + path; + if (verb == "get" || verb == "delete") { + fetchResponse = await fetch(url, { method: verb, headers, }); - } else if (verb == 'post' || verb == 'put') { - fetchResponse = await fetch(`${url}/${path}`, { + } else if (verb == "post" || verb == "put") { + fetchResponse = await fetch(url, { method: verb, headers, body: JSON.stringify(request), @@ -65,7 +89,7 @@ export class WaspClient { } if (!fetchResponse) { - throw new Error('No data was returned from the API'); + throw new Error("No data was returned from the API"); } try { @@ -73,7 +97,7 @@ export class WaspClient { } catch (err) { if (!fetchResponse.ok) { const text = await fetchResponse.text(); - throw new Error(err.message + ' --- ' + text); + throw new Error(err.message + " --- " + text); } } } catch (err) { @@ -82,6 +106,6 @@ export class WaspClient { ); } - return { body: response, response: fetchResponse }; + return {body: response, response: fetchResponse}; } } diff --git a/packages/vm/wasmlib/ts/wasmlib/package-lock.json b/packages/vm/wasmlib/ts/wasmlib/package-lock.json new file mode 100644 index 0000000000..b0e4152706 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmlib/package-lock.json @@ -0,0 +1,58 @@ +{ + "name": "wasmlib", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@assemblyscript/loader": { + "version": "0.19.22", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.22.tgz", + "integrity": "sha512-ksrMVpPOatD7ZzXCw+c/g3zId5rm8MMnQe7P32dH/qtDrgT9SbQjJYEngRP0YhRF0qrBCga2PtpID7arqphGyg==" + }, + "assemblyscript": { + "version": "0.19.22", + "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.19.22.tgz", + "integrity": "sha512-+Rclbx0+BI3qAe9fjc8XGbSUDaayTtjINnD19I4MmfpT2R43c9YTQERP36676shkPxb1fisDFZeSTL65Da8Q2g==", + "dev": true, + "requires": { + "binaryen": "102.0.0-nightly.20211028", + "long": "^5.2.0", + "source-map-support": "^0.5.20" + } + }, + "binaryen": { + "version": "102.0.0-nightly.20211028", + "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-102.0.0-nightly.20211028.tgz", + "integrity": "sha512-GCJBVB5exbxzzvyt8MGDv/MeUjs6gkXDvf4xOIItRBptYl0Tz5sm1o/uG95YK0L0VeG5ajDu3hRtkBP2kzqC5w==", + "dev": true + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "long": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", + "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } +} diff --git a/packages/vm/wasmlib/ts/wasmlib/package.json b/packages/vm/wasmlib/ts/wasmlib/package.json index 54a27cd14a..b3d0b9c01d 100644 --- a/packages/vm/wasmlib/ts/wasmlib/package.json +++ b/packages/vm/wasmlib/ts/wasmlib/package.json @@ -9,4 +9,4 @@ "devDependencies": { "assemblyscript": "^0.19.18" } -} \ No newline at end of file +} diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index c6f47ba0e7..296b91c9b5 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -69,20 +69,19 @@ $#if array funcArgSetterArray funcArgSetterBasic // ******************************* "funcPost": ` - public post(): wasmclient.RequestID { + public async post(): Promise { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs - return super.post(0x$hFuncName, $args); + return await super.post(0x$hFuncName, $args); } `, // ******************************* "viewCall": ` - public call(): $FuncName$+Results { + public async call(): Promise<$FuncName$+Results> { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs - super.call("$funcName", $args); - return new $FuncName$+Results(this.results()); + return new $FuncName$+Results(await this.callView("$funcName", $args)); } `, // ******************************* From 6b52a926dad14e34f50f70e226adc6641c7ba568 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sun, 2 Jan 2022 13:22:56 -0800 Subject: [PATCH 030/120] Small fixes --- .../fairroulette/frontend/package-lock.json | 6 -- .../wasm/testwasmlib/test/testwasmlib_test.go | 40 ++++++++++- packages/vm/wasmlib/go/wasmclient/service.go | 7 +- .../vm/wasmlib/ts/wasmclient/arguments.ts | 4 +- .../wasmlib/ts/wasmclient/package-lock.json | 68 ++++++++++++++++--- .../vm/wasmlib/ts/wasmclient/package.json | 9 ++- packages/vm/wasmlib/ts/wasmclient/service.ts | 4 +- .../vm/wasmlib/ts/wasmclient/tsconfig.json | 6 +- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 30 +++----- 9 files changed, 127 insertions(+), 47 deletions(-) diff --git a/contracts/wasm/fairroulette/frontend/package-lock.json b/contracts/wasm/fairroulette/frontend/package-lock.json index 20257cd895..cccc8d9e98 100644 --- a/contracts/wasm/fairroulette/frontend/package-lock.json +++ b/contracts/wasm/fairroulette/frontend/package-lock.json @@ -4245,12 +4245,6 @@ "glob": "^7.1.2" } }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", - "dev": true - }, "tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_test.go index a1e3bdf9b2..992d98a1a6 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_test.go @@ -315,7 +315,7 @@ func TestMultiRandom(t *testing.T) { // these values are usually available from elsewhere const ( mySeed = "6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv" - myChainID = "qjA8Ybw4WijnmGUqDtNcPhAxFymjQKepNyyfp5BUGsWP" + myChainID = "jn52vSuUUYY22T1mV2ny14EADYBu3ofyewLRSsVRnjpz" ) func setupClient(t *testing.T) *testwasmlibclient.TestWasmLibService { @@ -376,3 +376,41 @@ func TestClientRandom(t *testing.T) { require.GreaterOrEqual(t, res.Random(), int64(0)) fmt.Println("Random: ", res.Random()) } + +func TestClientArray(t *testing.T) { + svc := setupClient(t) + + v := svc.ArrayLength() + v.Name("Bands") + res := v.Call() + require.NoError(t, v.Error()) + require.EqualValues(t, 0, res.Length()) + + f := svc.ArraySet() + f.Name("Bands") + f.Index(0) + f.Value("Dire Straits") + req := f.Post() + require.NoError(t, req.Error()) + err := svc.WaitRequest(req) + require.NoError(t, err) + + v = svc.ArrayLength() + v.Name("Bands") + res = v.Call() + require.NoError(t, v.Error()) + require.EqualValues(t, 1, res.Length()) + + c := svc.ArrayClear() + c.Name("Bands") + req = c.Post() + require.NoError(t, req.Error()) + err = svc.WaitRequest(req) + require.NoError(t, err) + + v = svc.ArrayLength() + v.Name("Bands") + res = v.Call() + require.NoError(t, v.Error()) + require.EqualValues(t, 0, res.Length()) +} diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go index 6bb886f021..d870a6934a 100644 --- a/packages/vm/wasmlib/go/wasmclient/service.go +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -45,11 +45,10 @@ func (s *Service) AsClientView() ClientView { } func (s *Service) CallView(viewName string, args *Arguments) (dict.Dict, error) { - arg := dict.Dict(nil) - if args != nil { - arg = args.args + if args == nil { + args = &Arguments{} } - return s.waspClient.CallView(s.chainID, s.scHname, viewName, arg) + return s.waspClient.CallView(s.chainID, s.scHname, viewName, args.args) } func (s *Service) PostRequest(hFuncName uint32, args *Arguments, transfer *Transfer, keyPair *ed25519.KeyPair) Request { diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 553dcab5e2..069f41791a 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -125,12 +125,12 @@ export class Arguments { this.set(key, bytes); } - // Encode returns a byte array that encodes the Transfer as follows: + // Encode returns a byte array that encodes the Arguments as follows: // Sort all keys in ascending order (very important, because this data // will be part of the data that will be signed, so the order needs to // be 100% deterministic). Then emit the 4-byte argument count. // Next for each argument emit the 2-byte key length, the key prepended - // with this minus sign, the 4-byte value length, and then the value bytes. + // with the minus sign, the 4-byte value length, and then the value bytes. encode(): wasmclient.Bytes { let keys = new Array(); for (const key of this.args.keys()) { diff --git a/packages/vm/wasmlib/ts/wasmclient/package-lock.json b/packages/vm/wasmlib/ts/wasmclient/package-lock.json index b091c0f6b9..9cbaa6c8a4 100644 --- a/packages/vm/wasmlib/ts/wasmclient/package-lock.json +++ b/packages/vm/wasmlib/ts/wasmclient/package-lock.json @@ -4,6 +4,21 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/node": { + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.6.tgz", + "integrity": "sha512-+XBAjfZmmivILUzO0HwBJoYkAyyySSLg5KCGBDFLomJo0sV6szvVLAf4ANZZ0pfWzgEds5KmGLG9D5hfEqOhaA==", + "dev": true + }, + "@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -16,16 +31,25 @@ "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==", "dev": true }, - "nanoevents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/nanoevents/-/nanoevents-6.0.2.tgz", - "integrity": "sha512-FRS2otuFcPPYDPYViNWQ42+1iZqbXydinkRHTHFxrF4a1CpBfmydR9zkI44WSXAXCyPrkcGtPk5CnpW6Y3lFKQ==", + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", "dev": true }, "tweetnacl": { @@ -33,6 +57,34 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "dev": true + }, + "typescript": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "ws": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", + "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", + "dev": true } } } diff --git a/packages/vm/wasmlib/ts/wasmclient/package.json b/packages/vm/wasmlib/ts/wasmclient/package.json index f88b497c51..d3532344e8 100644 --- a/packages/vm/wasmlib/ts/wasmclient/package.json +++ b/packages/vm/wasmlib/ts/wasmclient/package.json @@ -5,10 +5,13 @@ "author": "Eric Hop", "dependencies": {}, "devDependencies": { + "@types/ws": "^7.4.7", + "node-fetch": "^2.0.0", + "ws": "^8.2.2", "base64-js": "^1.5.1", "blakejs": "^1.1.1", - "nanoevents": "^6.0.0", - "tslib": "^2.0.0", - "tweetnacl": "^1.0.3" + "ieee754": "^1.2.1", + "tweetnacl": "^1.0.3", + "typescript": "^4.0.0" } } diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 4c22d19c85..cf469ef079 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -28,7 +28,7 @@ export class Service { this.chainId, this.scHname.toString(16), viewName, - args, + args.encode(), ); } @@ -48,7 +48,7 @@ export class Service { const hash = Hash.from(buf); const requestID = Buffer.concat([hash, Buffer.alloc(2)]); - await this.waspClient.postOffLedgerRequest(this.chainId, buf); + await this.waspClient.postRequest(this.chainId, buf); return Base58.encode(requestID); } diff --git a/packages/vm/wasmlib/ts/wasmclient/tsconfig.json b/packages/vm/wasmlib/ts/wasmclient/tsconfig.json index e86bc66ee4..bcca834d03 100644 --- a/packages/vm/wasmlib/ts/wasmclient/tsconfig.json +++ b/packages/vm/wasmlib/ts/wasmclient/tsconfig.json @@ -1,10 +1,12 @@ { "compilerOptions": { "module": "commonjs", - "lib": ["es2020", "dom"], + "lib": ["es2020", "esnext", "dom"], "target": "es2020", - "sourceMap": true + "sourceMap": true, + "allowSyntheticDefaultImports": true }, + "preferBuiltins": true, "exclude": [ "node_modules" ], diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 470bd3a0ae..e3f411f39c 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -32,12 +32,13 @@ export class WaspClient { this.waspAPI = "http://" + waspAPI; } - //TODO args - public async callView(chainID: string, contractHName: string, entryPoint: string, args: wasmclient.Arguments): Promise { + public async callView(chainID: string, contractHName: string, entryPoint: string, args: Buffer): Promise { + const request = {Request: args.toString("base64")}; const result = await this.sendRequest( "get", "/chain/" + chainID + "/contract/ " + contractHName + "/callview/" + entryPoint, - ); + request + ); const res = new wasmclient.Results(); if (result.body.Items) { for (let item of result.body.Items) { @@ -49,7 +50,7 @@ export class WaspClient { return res; } - public async postOffLedgerRequest(chainID: string, offLedgerRequest: Buffer): Promise { + public async postRequest(chainID: string, offLedgerRequest: Buffer): Promise { const request = {Request: offLedgerRequest.toString("base64")}; await this.sendRequest( "post", @@ -75,18 +76,11 @@ export class WaspClient { try { const url = this.waspAPI + path; - if (verb == "get" || verb == "delete") { - fetchResponse = await fetch(url, { - method: verb, - headers, - }); - } else if (verb == "post" || verb == "put") { - fetchResponse = await fetch(url, { - method: verb, - headers, - body: JSON.stringify(request), - }); - } + fetchResponse = await fetch(url, { + method: verb, + headers, + body: JSON.stringify(request), + }); if (!fetchResponse) { throw new Error("No data was returned from the API"); @@ -101,9 +95,7 @@ export class WaspClient { } } } catch (err) { - throw new Error( - `The application is not able to complete the request, due to the following error:\n\n${err.message}`, - ); + throw new Error("sendRequest: " + err.message); } return {body: response, response: fetchResponse}; From 95ea10c072d130706700fd1b34a25beca054d783 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sun, 2 Jan 2022 14:33:02 -0800 Subject: [PATCH 031/120] Remove wasmclient's dependency on wasmlib --- .../vm/wasmlib/go/wasmclient/arguments.go | 15 ++++--- packages/vm/wasmlib/go/wasmclient/results.go | 39 +++++++++---------- packages/vm/wasmlib/go/wasmclient/types.go | 21 ++++++++++ .../vm/wasmlib/ts/wasmclient/arguments.ts | 15 ++++--- packages/vm/wasmlib/ts/wasmclient/results.ts | 39 +++++++++---------- packages/vm/wasmlib/ts/wasmclient/types.ts | 17 ++++++++ 6 files changed, 90 insertions(+), 56 deletions(-) diff --git a/packages/vm/wasmlib/go/wasmclient/arguments.go b/packages/vm/wasmlib/go/wasmclient/arguments.go index f88b0c83bc..a06e391488 100644 --- a/packages/vm/wasmlib/go/wasmclient/arguments.go +++ b/packages/vm/wasmlib/go/wasmclient/arguments.go @@ -8,7 +8,6 @@ import ( "github.com/iotaledger/wasp/packages/kv" "github.com/iotaledger/wasp/packages/kv/dict" - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) // The Arguments struct is used to gather all arguments for a smart @@ -26,7 +25,7 @@ func (a *Arguments) set(key string, val []byte) { func (a *Arguments) setBase58(key, val string, typeID int32) { bytes := Base58Decode(val) - if len(bytes) != int(wasmlib.TypeSizes[typeID]) { + if len(bytes) != int(TypeSizes[typeID]) { panic("invalid byte size") } a.set(key, bytes) @@ -46,11 +45,11 @@ func (a *Arguments) Mandatory(key string) { } func (a *Arguments) SetAddress(key string, val Address) { - a.setBase58(key, string(val), wasmlib.TYPE_ADDRESS) + a.setBase58(key, string(val), TYPE_ADDRESS) } func (a *Arguments) SetAgentID(key string, val AgentID) { - a.setBase58(key, string(val), wasmlib.TYPE_AGENT_ID) + a.setBase58(key, string(val), TYPE_AGENT_ID) } func (a *Arguments) SetBool(key string, val bool) { @@ -66,15 +65,15 @@ func (a *Arguments) SetBytes(key string, val []byte) { } func (a *Arguments) SetColor(key string, val Color) { - a.setBase58(key, string(val), wasmlib.TYPE_COLOR) + a.setBase58(key, string(val), TYPE_COLOR) } func (a *Arguments) SetChainID(key string, val ChainID) { - a.setBase58(key, string(val), wasmlib.TYPE_CHAIN_ID) + a.setBase58(key, string(val), TYPE_CHAIN_ID) } func (a *Arguments) SetHash(key string, val Hash) { - a.setBase58(key, string(val), wasmlib.TYPE_HASH) + a.setBase58(key, string(val), TYPE_HASH) } func (a *Arguments) SetHname(key string, val Hname) { @@ -98,7 +97,7 @@ func (a *Arguments) SetInt64(key string, val int64) { } func (a *Arguments) SetRequestID(key string, val RequestID) { - a.setBase58(key, string(val), wasmlib.TYPE_REQUEST_ID) + a.setBase58(key, string(val), TYPE_REQUEST_ID) } func (a *Arguments) SetString(key, val string) { diff --git a/packages/vm/wasmlib/go/wasmclient/results.go b/packages/vm/wasmlib/go/wasmclient/results.go index 04f987225a..4f392cf39d 100644 --- a/packages/vm/wasmlib/go/wasmclient/results.go +++ b/packages/vm/wasmlib/go/wasmclient/results.go @@ -6,7 +6,6 @@ package wasmclient import ( "github.com/iotaledger/wasp/packages/kv" "github.com/iotaledger/wasp/packages/kv/dict" - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" ) // The Results struct is used to gather all arguments for a smart @@ -21,7 +20,7 @@ func (r Results) Exists(key string) bool { } func (r Results) get(key string, typeID int32) []byte { - size := wasmlib.TypeSizes[typeID] + size := TypeSizes[typeID] bytes, ok := r.res[kv.Key(key)] if ok { if size != 0 && len(bytes) != int(size) { @@ -38,75 +37,75 @@ func (r Results) getBase58(key string, typeID int32) string { } func (r Results) GetAddress(key string) Address { - return Address(r.getBase58(key, wasmlib.TYPE_ADDRESS)) + return Address(r.getBase58(key, TYPE_ADDRESS)) } func (r Results) GetAgentID(key string) AgentID { - return AgentID(r.getBase58(key, wasmlib.TYPE_AGENT_ID)) + return AgentID(r.getBase58(key, TYPE_AGENT_ID)) } func (r Results) GetBytes(key string) []byte { - return r.get(key, wasmlib.TYPE_BYTES) + return r.get(key, TYPE_BYTES) } func (r Results) GetBool(key string) bool { - return r.get(key, wasmlib.TYPE_BOOL)[0] != 0 + return r.get(key, TYPE_BOOL)[0] != 0 } func (r Results) GetChainID(key string) ChainID { - return ChainID(r.getBase58(key, wasmlib.TYPE_CHAIN_ID)) + return ChainID(r.getBase58(key, TYPE_CHAIN_ID)) } func (r Results) GetColor(key string) Color { - return Color(r.getBase58(key, wasmlib.TYPE_COLOR)) + return Color(r.getBase58(key, TYPE_COLOR)) } func (r Results) GetHash(key string) Hash { - return Hash(r.getBase58(key, wasmlib.TYPE_HASH)) + return Hash(r.getBase58(key, TYPE_HASH)) } func (r Results) GetHname(key string) Hname { - return Hname(r.getUint64(key, wasmlib.TYPE_HNAME)) + return Hname(r.getUint64(key, TYPE_HNAME)) } func (r Results) GetInt8(key string) int8 { - return int8(r.get(key, wasmlib.TYPE_INT8)[0]) + return int8(r.get(key, TYPE_INT8)[0]) } func (r Results) GetInt16(key string) int16 { - return int16(r.getUint64(key, wasmlib.TYPE_INT16)) + return int16(r.getUint64(key, TYPE_INT16)) } func (r Results) GetInt32(key string) int32 { - return int32(r.getUint64(key, wasmlib.TYPE_INT32)) + return int32(r.getUint64(key, TYPE_INT32)) } func (r Results) GetInt64(key string) int64 { - return int64(r.getUint64(key, wasmlib.TYPE_INT64)) + return int64(r.getUint64(key, TYPE_INT64)) } func (r Results) GetRequestID(key string) RequestID { - return RequestID(r.getBase58(key, wasmlib.TYPE_REQUEST_ID)) + return RequestID(r.getBase58(key, TYPE_REQUEST_ID)) } func (r Results) GetString(key string) string { - return string(r.get(key, wasmlib.TYPE_STRING)) + return string(r.get(key, TYPE_STRING)) } func (r Results) GetUint8(key string) uint8 { - return r.get(key, wasmlib.TYPE_INT8)[0] + return r.get(key, TYPE_INT8)[0] } func (r Results) GetUint16(key string) uint16 { - return uint16(r.getUint64(key, wasmlib.TYPE_INT16)) + return uint16(r.getUint64(key, TYPE_INT16)) } func (r Results) GetUint32(key string) uint32 { - return uint32(r.getUint64(key, wasmlib.TYPE_INT32)) + return uint32(r.getUint64(key, TYPE_INT32)) } func (r Results) GetUint64(key string) uint64 { - return r.getUint64(key, wasmlib.TYPE_INT64) + return r.getUint64(key, TYPE_INT64) } func (r Results) getUint64(key string, typeID int32) uint64 { diff --git a/packages/vm/wasmlib/go/wasmclient/types.go b/packages/vm/wasmlib/go/wasmclient/types.go index cd77b7a0ab..87e618abb9 100644 --- a/packages/vm/wasmlib/go/wasmclient/types.go +++ b/packages/vm/wasmlib/go/wasmclient/types.go @@ -3,6 +3,25 @@ package wasmclient +//nolint:revive +const ( + TYPE_ADDRESS int32 = 1 + TYPE_AGENT_ID int32 = 2 + TYPE_BOOL int32 = 3 + TYPE_BYTES int32 = 4 + TYPE_CHAIN_ID int32 = 5 + TYPE_COLOR int32 = 6 + TYPE_HASH int32 = 7 + TYPE_HNAME int32 = 8 + TYPE_INT8 int32 = 9 + TYPE_INT16 int32 = 10 + TYPE_INT32 int32 = 11 + TYPE_INT64 int32 = 12 + TYPE_MAP int32 = 13 + TYPE_REQUEST_ID int32 = 14 + TYPE_STRING int32 = 15 +) + type ( Address string AgentID string @@ -12,3 +31,5 @@ type ( Hname uint32 RequestID string ) + +var TypeSizes = [...]uint8{0, 33, 37, 1, 0, 33, 32, 32, 4, 1, 2, 4, 8, 0, 34, 0} diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 069f41791a..7909025fcc 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import * as wasmclient from "./index" -import * as wasmlib from "../wasmlib" import {Base58} from "./crypto"; import {Buffer} from "./buffer"; @@ -17,7 +16,7 @@ export class Arguments { private setBase58(key: string, val: string, typeID: wasmclient.Int32): void { let bytes = Base58.decode(val); - if (bytes.length != wasmlib.TYPE_SIZES[typeID]) { + if (bytes.length != wasmclient.TYPE_SIZES[typeID]) { wasmclient.panic("invalid byte size"); } this.set(key, bytes); @@ -34,11 +33,11 @@ export class Arguments { } setAddress(key: string, val: wasmclient.AgentID): void { - this.setBase58(key, val, wasmlib.TYPE_ADDRESS); + this.setBase58(key, val, wasmclient.TYPE_ADDRESS); } setAgentID(key: string, val: wasmclient.AgentID): void { - this.setBase58(key, val, wasmlib.TYPE_AGENT_ID); + this.setBase58(key, val, wasmclient.TYPE_AGENT_ID); } setBool(key: string, val: boolean): void { @@ -54,15 +53,15 @@ export class Arguments { } setColor(key: string, val: wasmclient.Color): void { - this.setBase58(key, val, wasmlib.TYPE_COLOR); + this.setBase58(key, val, wasmclient.TYPE_COLOR); } setChainID(key: string, val: wasmclient.ChainID): void { - this.setBase58(key, val, wasmlib.TYPE_CHAIN_ID); + this.setBase58(key, val, wasmclient.TYPE_CHAIN_ID); } setHash(key: string, val: wasmclient.Hash): void { - this.setBase58(key, val, wasmlib.TYPE_HASH); + this.setBase58(key, val, wasmclient.TYPE_HASH); } setHname(key: string, val: wasmclient.Hname): void { @@ -94,7 +93,7 @@ export class Arguments { } setRequestID(key: string, val: wasmclient.RequestID): void { - this.setBase58(key, val, wasmlib.TYPE_REQUEST_ID); + this.setBase58(key, val, wasmclient.TYPE_REQUEST_ID); } setString(key: string, val: string): void { diff --git a/packages/vm/wasmlib/ts/wasmclient/results.ts b/packages/vm/wasmlib/ts/wasmclient/results.ts index edba99ebbf..120fa84914 100644 --- a/packages/vm/wasmlib/ts/wasmclient/results.ts +++ b/packages/vm/wasmlib/ts/wasmclient/results.ts @@ -1,7 +1,6 @@ // The Results struct is used to gather all arguments for a smart // contract function call and encode it into a deterministic byte array import * as wasmclient from "./index"; -import * as wasmlib from "../wasmlib"; import {Base58} from "./crypto"; import {Buffer} from "./buffer"; @@ -21,7 +20,7 @@ export class Results { } private get(key: string, typeID: wasmclient.Int32): wasmclient.Bytes { - let size = wasmlib.TYPE_SIZES[typeID]; + let size = wasmclient.TYPE_SIZES[typeID]; let bytes = this.res.get(key); if (bytes !== undefined) { if (size != 0 && bytes.length != size) { @@ -38,75 +37,75 @@ export class Results { } getAddress(key: string): wasmclient.Address { - return this.getBase58(key, wasmlib.TYPE_ADDRESS); + return this.getBase58(key, wasmclient.TYPE_ADDRESS); } getAgentID(key: string): wasmclient.AgentID { - return this.getBase58(key, wasmlib.TYPE_AGENT_ID); + return this.getBase58(key, wasmclient.TYPE_AGENT_ID); } getBytes(key: string): wasmclient.Bytes { - return this.get(key, wasmlib.TYPE_BYTES) + return this.get(key, wasmclient.TYPE_BYTES) } getBool(key: string): wasmclient.Bool { - return this.get(key, wasmlib.TYPE_BOOL)[0] != 0; + return this.get(key, wasmclient.TYPE_BOOL)[0] != 0; } getChainID(key: string): wasmclient.ChainID { - return this.getBase58(key, wasmlib.TYPE_CHAIN_ID); + return this.getBase58(key, wasmclient.TYPE_CHAIN_ID); } getColor(key: string): wasmclient.Color { - return this.getBase58(key, wasmlib.TYPE_COLOR); + return this.getBase58(key, wasmclient.TYPE_COLOR); } getHash(key: string): wasmclient.Hash { - return this.getBase58(key, wasmlib.TYPE_HASH); + return this.getBase58(key, wasmclient.TYPE_HASH); } getHname(key: string): wasmclient.Hname { - return this.get(key, wasmlib.TYPE_HNAME).readUInt32LE(0); + return this.get(key, wasmclient.TYPE_HNAME).readUInt32LE(0); } getInt8(key: string): wasmclient.Int8 { - return this.get(key, wasmlib.TYPE_INT8).readInt8(0); + return this.get(key, wasmclient.TYPE_INT8).readInt8(0); } getInt16(key: string): wasmclient.Int16 { - return this.get(key, wasmlib.TYPE_INT16).readInt16LE(0); + return this.get(key, wasmclient.TYPE_INT16).readInt16LE(0); } getInt32(key: string): wasmclient.Int32 { - return this.get(key, wasmlib.TYPE_INT32).readInt32LE(0); + return this.get(key, wasmclient.TYPE_INT32).readInt32LE(0); } getInt64(key: string): wasmclient.Int64 { - return this.get(key, wasmlib.TYPE_INT64).readBigInt64LE(0); + return this.get(key, wasmclient.TYPE_INT64).readBigInt64LE(0); } getRequestID(key: string): wasmclient.RequestID { - return this.getBase58(key, wasmlib.TYPE_REQUEST_ID); + return this.getBase58(key, wasmclient.TYPE_REQUEST_ID); } getString(key: string): wasmclient.String { - return this.get(key, wasmlib.TYPE_STRING).toString(); + return this.get(key, wasmclient.TYPE_STRING).toString(); } getUint8(key: string): wasmclient.Uint8 { - return this.get(key, wasmlib.TYPE_INT8).readUInt8(0); + return this.get(key, wasmclient.TYPE_INT8).readUInt8(0); } getUint16(key: string): wasmclient.Uint16 { - return this.get(key, wasmlib.TYPE_INT16).readUInt16LE(0); + return this.get(key, wasmclient.TYPE_INT16).readUInt16LE(0); } getUint32(key: string): wasmclient.Uint32 { - return this.get(key, wasmlib.TYPE_INT32).readUInt32LE(0); + return this.get(key, wasmclient.TYPE_INT32).readUInt32LE(0); } getUint64(key: string): wasmclient.Uint64 { - return this.get(key, wasmlib.TYPE_INT64).readBigUInt64LE(0); + return this.get(key, wasmclient.TYPE_INT64).readBigUInt64LE(0); } // TODO Decode() from view call response into map diff --git a/packages/vm/wasmlib/ts/wasmclient/types.ts b/packages/vm/wasmlib/ts/wasmclient/types.ts index 821b200ace..95484471ef 100644 --- a/packages/vm/wasmlib/ts/wasmclient/types.ts +++ b/packages/vm/wasmlib/ts/wasmclient/types.ts @@ -3,6 +3,22 @@ import {Buffer} from "./buffer"; +export const TYPE_ADDRESS = 1; +export const TYPE_AGENT_ID = 2; +export const TYPE_BOOL = 3; +export const TYPE_BYTES = 4; +export const TYPE_CHAIN_ID = 5; +export const TYPE_COLOR = 6; +export const TYPE_HASH = 7; +export const TYPE_HNAME = 8; +export const TYPE_INT8 = 9; +export const TYPE_INT16 = 10; +export const TYPE_INT32 = 11; +export const TYPE_INT64 = 12; +export const TYPE_MAP = 13; +export const TYPE_REQUEST_ID = 14; +export const TYPE_STRING = 15; + export type Address = string; export type AgentID = string; export type Bool = boolean; @@ -22,6 +38,7 @@ export type Uint16 = number; export type Uint32 = number; export type Uint64 = bigint; +export const TYPE_SIZES = new Uint8Array([0, 33, 37, 1, 0, 33, 32, 32, 4, 1, 2, 4, 8, 0, 34, 0]); export function panic(err: string) { throw new Error(err); From eff67500e8a8cf7bc6b8801ce3df85a7116f6f6a Mon Sep 17 00:00:00 2001 From: shawkyz Date: Mon, 3 Jan 2022 00:24:29 +0100 Subject: [PATCH 032/120] use configuration type to pass configs --- .../vm/wasmlib/ts/wasmclient/arguments.ts | 26 +++++++------- .../vm/wasmlib/ts/wasmclient/config.dev.js | 10 ------ .../vm/wasmlib/ts/wasmclient/configuration.ts | 27 +++++++++++++++ packages/vm/wasmlib/ts/wasmclient/results.ts | 4 +-- packages/vm/wasmlib/ts/wasmclient/service.ts | 34 ++++++++++--------- .../vm/wasmlib/ts/wasmclient/serviceclient.ts | 16 ++++++--- packages/vm/wasmlib/ts/wasmclient/transfer.ts | 4 +-- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 7 ++-- .../generator/tsclienttemplates/events.go | 6 ++-- .../generator/tsclienttemplates/service.go | 8 ++--- 10 files changed, 85 insertions(+), 57 deletions(-) delete mode 100644 packages/vm/wasmlib/ts/wasmclient/config.dev.js create mode 100644 packages/vm/wasmlib/ts/wasmclient/configuration.ts diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 7909025fcc..b8a70296f9 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -15,7 +15,7 @@ export class Arguments { } private setBase58(key: string, val: string, typeID: wasmclient.Int32): void { - let bytes = Base58.decode(val); + const bytes = Base58.decode(val); if (bytes.length != wasmclient.TYPE_SIZES[typeID]) { wasmclient.panic("invalid byte size"); } @@ -41,7 +41,7 @@ export class Arguments { } setBool(key: string, val: boolean): void { - let bytes = Buffer.alloc(1); + const bytes = Buffer.alloc(1); if (val) { bytes.writeUInt8(1, 0); } @@ -69,25 +69,25 @@ export class Arguments { } setInt8(key: string, val: wasmclient.Int8): void { - let bytes = Buffer.alloc(1); + const bytes = Buffer.alloc(1); bytes.writeInt8(val, 0); this.set(key, bytes); } setInt16(key: string, val: wasmclient.Int16): void { - let bytes = Buffer.alloc(2); + const bytes = Buffer.alloc(2); bytes.writeInt16LE(val, 0); this.set(key, bytes); } setInt32(key: string, val: wasmclient.Int32): void { - let bytes = Buffer.alloc(4); + const bytes = Buffer.alloc(4); bytes.writeInt32LE(val, 0); this.set(key, bytes); } setInt64(key: string, val: wasmclient.Int64): void { - let bytes = Buffer.alloc(8); + const bytes = Buffer.alloc(8); bytes.writeBigInt64LE(val, 0); this.set(key, bytes); } @@ -101,25 +101,25 @@ export class Arguments { } setUint8(key: string, val: wasmclient.Uint8): void { - let bytes = Buffer.alloc(1); + const bytes = Buffer.alloc(1); bytes.writeUInt8(val, 0); this.set(key, bytes); } setUint16(key: string, val: wasmclient.Uint16): void { - let bytes = Buffer.alloc(2); + const bytes = Buffer.alloc(2); bytes.writeUInt16LE(val, 0); this.set(key, bytes); } setUint32(key: string, val: wasmclient.Uint32): void { - let bytes = Buffer.alloc(4); + const bytes = Buffer.alloc(4); bytes.writeUInt32LE(val, 0); this.set(key, bytes); } setUint64(key: string, val: wasmclient.Uint64): void { - let bytes = Buffer.alloc(8); + const bytes = Buffer.alloc(8); bytes.writeBigUInt64LE(val, 0); this.set(key, bytes); } @@ -131,7 +131,7 @@ export class Arguments { // Next for each argument emit the 2-byte key length, the key prepended // with the minus sign, the 4-byte value length, and then the value bytes. encode(): wasmclient.Bytes { - let keys = new Array(); + const keys = new Array(); for (const key of this.args.keys()) { keys.push(key); } @@ -140,10 +140,10 @@ export class Arguments { let buf = Buffer.alloc(0); buf.writeUInt32LE(keys.length, 0); for (const key of keys) { - let keyBuf = Buffer.from("-" + key); + const keyBuf = Buffer.from("-" + key); buf.writeUInt16LE(keyBuf.length, buf.length); buf = Buffer.concat([buf, keyBuf]); - let valBuf = this.args.get(key); + const valBuf = this.args.get(key); buf.writeUInt32LE(valBuf.length, buf.length); buf = Buffer.concat([buf, valBuf]); } diff --git a/packages/vm/wasmlib/ts/wasmclient/config.dev.js b/packages/vm/wasmlib/ts/wasmclient/config.dev.js deleted file mode 100644 index fe795a7f20..0000000000 --- a/packages/vm/wasmlib/ts/wasmclient/config.dev.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - // Either set a default seed, or set it to undefined to generate a new seed each page load - seed: undefined, - waspWebSocketUrl: 'ws://127.0.0.1:9090/chain/%chainId/ws', - waspApiUrl: 'http://127.0.0.1:9090', - goshimmerApiUrl: 'http://127.0.0.1:8080', - chainId: 'pG9BvsC7h1tYPtqQityhH1qjCaN8A65mGWoxDQgSSbRt', - googleAnalytics: undefined, - contractName: 'fairroulette' -}; diff --git a/packages/vm/wasmlib/ts/wasmclient/configuration.ts b/packages/vm/wasmlib/ts/wasmclient/configuration.ts new file mode 100644 index 0000000000..eb5a7bc4a1 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/configuration.ts @@ -0,0 +1,27 @@ +import { Buffer } from './buffer'; + +export interface IConfiguration { + seed: Buffer | null; + waspWebSocketUrl: string; + waspApiUrl: string; + goShimmerApiUrl: string; +} + +export class Configuration implements IConfiguration { + seed: Buffer | null = null; + waspWebSocketUrl: string = ''; + waspApiUrl: string = ''; + goShimmerApiUrl: string = ''; + chainId: string = ''; + + constructor(configuration: IConfiguration) { + this.seed = configuration.seed; + this.waspWebSocketUrl = configuration.waspWebSocketUrl; + this.waspApiUrl = configuration.waspApiUrl; + this.goShimmerApiUrl = configuration.goShimmerApiUrl; + } + + public toString = (): string => { + return `Configuration : { seed: ${this.seed}, waspWebSocketUrl : ${this.waspWebSocketUrl}, waspApiUrl : ${this.waspApiUrl}, goShimmerApiUrl : ${this.goShimmerApiUrl}, chainId : ${this.chainId}}`; + }; +} diff --git a/packages/vm/wasmlib/ts/wasmclient/results.ts b/packages/vm/wasmlib/ts/wasmclient/results.ts index 120fa84914..dc90d5f173 100644 --- a/packages/vm/wasmlib/ts/wasmclient/results.ts +++ b/packages/vm/wasmlib/ts/wasmclient/results.ts @@ -20,8 +20,8 @@ export class Results { } private get(key: string, typeID: wasmclient.Int32): wasmclient.Bytes { - let size = wasmclient.TYPE_SIZES[typeID]; - let bytes = this.res.get(key); + const size = wasmclient.TYPE_SIZES[typeID]; + const bytes = this.res.get(key); if (bytes !== undefined) { if (size != 0 && bytes.length != size) { wasmclient.panic("invalid type size"); diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index cf469ef079..ddd828782b 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -8,24 +8,22 @@ import {Buffer} from "./buffer"; export type EventHandlers = { [key: string]: (message: string[]) => void }; export class Service { - private waspClient: wasmclient.WaspClient; + private serviceClient: wasmclient.ServiceClient; private webSocket: WebSocket; - private webSocketUrl: string; public keyPair: IKeyPair; private eventHandlers: EventHandlers; - public chainId: string; public scHname: wasmclient.Hname; + private waspWebSocketUrl: string; - constructor(client: wasmclient.ServiceClient, chainId: string, scHname: wasmclient.Hname, eventHandlers: EventHandlers) { - this.waspClient = client.waspClient; - this.chainId = chainId; + constructor(client: wasmclient.ServiceClient, scHname: wasmclient.Hname, eventHandlers: EventHandlers) { + this.serviceClient = client; this.scHname = scHname; - this.startEventHandlers(client.eventPort, eventHandlers); + this.configureWebSocketsEventHandlers(eventHandlers); } public async callView(viewName: string, args: wasmclient.Arguments): Promise { - return await this.waspClient.callView( - this.chainId, + return await this.serviceClient.waspClient.callView( + this.serviceClient.configuration.chainId, this.scHname.toString(16), viewName, args.encode(), @@ -34,7 +32,7 @@ export class Service { public async postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): Promise { // get request essence ready for signing - let essence = Base58.decode(this.chainId); + let essence = Base58.decode(this.serviceClient.configuration.chainId); essence.writeUInt32LE(this.scHname, essence.length); essence.writeUInt32LE(hFuncName, essence.length); essence = Buffer.concat([essence, args.encode(), keyPair.publicKey]); @@ -48,24 +46,28 @@ export class Service { const hash = Hash.from(buf); const requestID = Buffer.concat([hash, Buffer.alloc(2)]); - await this.waspClient.postRequest(this.chainId, buf); + await this.serviceClient.waspClient.postRequest(this.serviceClient.configuration.chainId, buf); return Base58.encode(requestID); } public async waitRequest(reqID: wasmclient.RequestID): Promise { - await this.waspClient.waitRequest(this.chainId, reqID); + await this.serviceClient.waspClient.waitRequest(this.serviceClient.configuration.chainId, reqID); } - private startEventHandlers(eventPort: string, eventHandlers: EventHandlers) { - this.webSocketUrl = "ws://" + eventPort + "/chain/" + this.chainId + "/ws"; + private configureWebSocketsEventHandlers(eventHandlers: EventHandlers) { this.eventHandlers = eventHandlers + + if(this.serviceClient.configuration.waspWebSocketUrl.startsWith("wss://") || this.serviceClient.configuration.waspWebSocketUrl.startsWith("ws://")) + this.waspWebSocketUrl = this.serviceClient.configuration.waspWebSocketUrl; + else + this.waspWebSocketUrl = "ws://" + this.serviceClient.configuration.waspWebSocketUrl; this.connectWebSocket(); } private connectWebSocket(): void { // eslint-disable-next-line no-console - console.log(`Connecting to Websocket => ${this.webSocketUrl}`); - this.webSocket = new WebSocket(this.webSocketUrl); + console.log(`Connecting to Websocket => ${this.waspWebSocketUrl}`); + this.webSocket = new WebSocket(this.waspWebSocketUrl); this.webSocket.addEventListener('message', (x) => this.handleIncomingMessage(x)); this.webSocket.addEventListener('close', () => setTimeout(this.connectWebSocket.bind(this), 1000)); } diff --git a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts index 421b39e93e..c648988713 100644 --- a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts @@ -2,18 +2,24 @@ // SPDX-License-Identifier: Apache-2.0 import * as wasmclient from "./index" +import {Configuration, IConfiguration} from "./configuration" export class ServiceClient { waspClient: wasmclient.WaspClient; - eventPort: string; + configuration: Configuration; - constructor(waspAPI: string, eventPort: string) { - this.waspClient = new wasmclient.WaspClient(waspAPI); - this.eventPort = eventPort; + constructor(configuration: Configuration) { + this.waspClient = new wasmclient.WaspClient(configuration.waspApiUrl); } static default(): ServiceClient { //TODO use TCP instead of websocket for event listener? - return new ServiceClient("127.0.0.1:9090", "127.0.0.1:9090"); // "127.0.0.1:5550"); + const defaultConfiguration : IConfiguration = { + seed: null, + waspWebSocketUrl: "ws://127.0.0.1:9090", + waspApiUrl: "127.0.0.1:9090", + goShimmerApiUrl: "" + }; + return new ServiceClient(new Configuration(defaultConfiguration)); // "127.0.0.1:5550"); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/transfer.ts b/packages/vm/wasmlib/ts/wasmclient/transfer.ts index 02ebc9883a..94b83e8ae2 100644 --- a/packages/vm/wasmlib/ts/wasmclient/transfer.ts +++ b/packages/vm/wasmlib/ts/wasmclient/transfer.ts @@ -13,7 +13,7 @@ export class Transfer { } static tokens(color: string, amount: wasmclient.Uint64): Transfer { - let transfer = new Transfer(); + const transfer = new Transfer(); transfer.set(color, amount); return transfer; } @@ -32,7 +32,7 @@ export class Transfer { // transfer count. Next for each color emit the 32-byte color value, // and then the 8-byte amount. encode(): wasmclient.Bytes { - let keys = new Array(); + const keys = new Array(); for (const [key, val] of this.xfer) { // filter out zero transfers if (val != BigInt(0)) { diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index e3f411f39c..6dedbc55e2 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -29,7 +29,10 @@ export class WaspClient { private waspAPI: string; constructor(waspAPI: string) { - this.waspAPI = "http://" + waspAPI; + if(waspAPI.startsWith("https://") || waspAPI.startsWith("http://")) + this.waspAPI = waspAPI; + else + this.waspAPI = "http://" + waspAPI; } public async callView(chainID: string, contractHName: string, entryPoint: string, args: Buffer): Promise { @@ -41,7 +44,7 @@ export class WaspClient { ); const res = new wasmclient.Results(); if (result.body.Items) { - for (let item of result.body.Items) { + for (const item of result.body.Items) { const key = Buffer.from(item.Key, "base64").toString(); const value = Buffer.from(item.Value, "base64"); res.res.set(key, value); diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go index c481b11540..fef1f40333 100644 --- a/tools/schema/generator/tsclienttemplates/events.go +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -3,7 +3,7 @@ package tsclienttemplates var eventsTs = map[string]string{ // ******************************* "events.ts": ` -import * as wasmclient from "wasmclient" +import * as wasmclient from "./wasmclient" import * as app from "./$package" export const eventHandlers: wasmclient.EventHandlers = { @@ -23,14 +23,14 @@ $#each event eventClassField } function on$PkgName$EvtName$+Thunk(message: string[]) { - let e = new Event$EvtName(message); + const e = new Event$EvtName(message); $#each event eventHandlerField app.on$PkgName$EvtName(e); } `, // ******************************* "eventClassField": ` - public $fldName: wasmclient.$FldType; + public $fldName: wasmclient.$FldType | undefined; `, // ******************************* "eventHandlerField": ` diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 296b91c9b5..92a3f95fb1 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -3,7 +3,7 @@ package tsclienttemplates var serviceTs = map[string]string{ // ******************************* "service.ts": ` -import * as wasmclient from "wasmclient" +import * as wasmclient from "./wasmclient" import * as events from "./events" $#each params constArg @@ -15,8 +15,8 @@ $#each func funcStruct export class $PkgName$+Service extends wasmclient.Service { - constructor(cl: wasmclient.ServiceClient, chainID: string) { - super(cl, chainID, 0x$hscName, events.eventHandlers); + constructor(cl: wasmclient.ServiceClient) { + super(cl, 0x$hscName, events.eventHandlers); } $#each func serviceFunction } @@ -133,7 +133,7 @@ $#if mandatory else callResultOptional "serviceFunction": ` public $funcName(): $FuncName$Kind { - return new $FuncName$Kind(this); + return new $FuncName$Kind(this); } `, } From e6f6c3e82d9e3039e024c4354384d6bba73c32ee Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Mon, 3 Jan 2022 02:43:34 +0100 Subject: [PATCH 033/120] Added support for strict compiler options to wasmclient --- .gitignore | 2 +- .../vm/wasmlib/ts/wasmclient/arguments.ts | 3 + .../vm/wasmlib/ts/wasmclient/clientfunc.ts | 12 +- .../vm/wasmlib/ts/wasmclient/crypto/hash.ts | 1 + .../wasmlib/ts/wasmclient/package-lock.json | 156 +++++++++++++++++- packages/vm/wasmlib/ts/wasmclient/service.ts | 10 +- .../vm/wasmlib/ts/wasmclient/serviceclient.ts | 1 + packages/vm/wasmlib/ts/wasmclient/transfer.ts | 4 +- .../vm/wasmlib/ts/wasmclient/tsconfig.json | 11 +- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 15 +- 10 files changed, 193 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index 16ec869941..14b019c3ab 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,6 @@ wasp-cli.json .DS_Store ./tools/wasp-cli/wasp-cli .docusaurus -node_modules +**/node_modules/ goshimmer.log /*.sh diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index b8a70296f9..569878254a 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -143,7 +143,10 @@ export class Arguments { const keyBuf = Buffer.from("-" + key); buf.writeUInt16LE(keyBuf.length, buf.length); buf = Buffer.concat([buf, keyBuf]); + const valBuf = this.args.get(key); + if(!valBuf) continue; + buf.writeUInt32LE(valBuf.length, buf.length); buf = Buffer.concat([buf, valBuf]); } diff --git a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts index 24bca456fb..82b06000c4 100644 --- a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts +++ b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts @@ -6,7 +6,7 @@ import {IKeyPair} from "./crypto"; export class ClientFunc { protected svc: wasmclient.Service; - private keyPair: IKeyPair; + private keyPair: IKeyPair | null = null; private xfer: wasmclient.Transfer = new wasmclient.Transfer(); constructor(svc: wasmclient.Service) { @@ -17,12 +17,14 @@ export class ClientFunc { // You can wait for the request to complete by using the returned RequestID // as parameter to Service.waitRequest() public async post(hFuncName: wasmclient.Hname, args: wasmclient.Arguments | null): Promise { - if (args == null) { + if (!args) args = new wasmclient.Arguments(); - } - if (this.keyPair == null) { + + if (!this.keyPair) this.keyPair = this.svc.keyPair; - } + + if (!this.keyPair) throw new Error("Key pair not defined"); + return await this.svc.postRequest(hFuncName, args, this.xfer, this.keyPair); } diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/hash.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/hash.ts index 078e92b2b1..fd29d3d50a 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/hash.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/hash.ts @@ -3,6 +3,7 @@ import {Buffer} from '../buffer'; export class Hash { public static from(bytes: Buffer): Buffer { + // @ts-ignore return blake2b(bytes, undefined, 32 /* Blake256 */); } } \ No newline at end of file diff --git a/packages/vm/wasmlib/ts/wasmclient/package-lock.json b/packages/vm/wasmlib/ts/wasmclient/package-lock.json index 9cbaa6c8a4..ec06b68e75 100644 --- a/packages/vm/wasmlib/ts/wasmclient/package-lock.json +++ b/packages/vm/wasmlib/ts/wasmclient/package-lock.json @@ -1,8 +1,159 @@ { "name": "wasmclient", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "wasmclient", + "version": "1.0.0", + "devDependencies": { + "@types/ws": "^7.4.7", + "base64-js": "^1.5.1", + "blakejs": "^1.1.1", + "ieee754": "^1.2.1", + "node-fetch": "^2.0.0", + "tweetnacl": "^1.0.3", + "typescript": "^4.0.0", + "ws": "^8.2.2" + } + }, + "node_modules/@types/node": { + "version": "17.0.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.6.tgz", + "integrity": "sha512-+XBAjfZmmivILUzO0HwBJoYkAyyySSLg5KCGBDFLomJo0sV6szvVLAf4ANZZ0pfWzgEds5KmGLG9D5hfEqOhaA==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/blakejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==", + "dev": true + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/node-fetch": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", + "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "dev": true + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", + "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/ws": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", + "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, "dependencies": { "@types/node": { "version": "17.0.6", @@ -84,7 +235,8 @@ "version": "8.4.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.0.tgz", "integrity": "sha512-IHVsKe2pjajSUIl4KYMQOdlyliovpEPquKkqbwswulszzI7r0SfQrxnXdWAEqOlDCLrVSJzo+O1hAwdog2sKSQ==", - "dev": true + "dev": true, + "requires": {} } } } diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index ddd828782b..d3262f06d4 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -9,11 +9,11 @@ export type EventHandlers = { [key: string]: (message: string[]) => void }; export class Service { private serviceClient: wasmclient.ServiceClient; - private webSocket: WebSocket; - public keyPair: IKeyPair; - private eventHandlers: EventHandlers; + private webSocket: WebSocket | null = null; + public keyPair: IKeyPair | null = null; + private eventHandlers: EventHandlers | null = null; public scHname: wasmclient.Hname; - private waspWebSocketUrl: string; + private waspWebSocketUrl: string = ""; constructor(client: wasmclient.ServiceClient, scHname: wasmclient.Hname, eventHandlers: EventHandlers) { this.serviceClient = client; @@ -80,7 +80,7 @@ export class Service { } const topics = msg[3].split('|'); const topic = topics[0]; - if (this.eventHandlers[topic] != undefined) { + if (this.eventHandlers && this.eventHandlers[topic] != undefined) { this.eventHandlers[topic](msg.slice(1)); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts index c648988713..f05fc2ed36 100644 --- a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts @@ -9,6 +9,7 @@ export class ServiceClient { configuration: Configuration; constructor(configuration: Configuration) { + this.configuration = configuration; this.waspClient = new wasmclient.WaspClient(configuration.waspApiUrl); } diff --git a/packages/vm/wasmlib/ts/wasmclient/transfer.ts b/packages/vm/wasmlib/ts/wasmclient/transfer.ts index 94b83e8ae2..f1bea48ef9 100644 --- a/packages/vm/wasmlib/ts/wasmclient/transfer.ts +++ b/packages/vm/wasmlib/ts/wasmclient/transfer.ts @@ -45,7 +45,9 @@ export class Transfer { buf.writeUInt32LE(keys.length, 0); for (const key of keys) { buf = Buffer.concat([buf, key]); - buf.writeBigUInt64LE(this.xfer.get(key), buf.length); + const value = this.xfer.get(key); + if(value === undefined || value === null) continue; + buf.writeBigUInt64LE(value, buf.length); } return buf; } diff --git a/packages/vm/wasmlib/ts/wasmclient/tsconfig.json b/packages/vm/wasmlib/ts/wasmclient/tsconfig.json index bcca834d03..1b47fa376c 100644 --- a/packages/vm/wasmlib/ts/wasmclient/tsconfig.json +++ b/packages/vm/wasmlib/ts/wasmclient/tsconfig.json @@ -1,12 +1,19 @@ { "compilerOptions": { - "module": "commonjs", + "strict": true, "lib": ["es2020", "esnext", "dom"], "target": "es2020", + "module": "esnext", "sourceMap": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "moduleResolution": "node", + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + }, }, "preferBuiltins": true, + "include": ["./**/*.ts"], "exclude": [ "node_modules" ], diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 6dedbc55e2..b83e43de99 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -43,7 +43,8 @@ export class WaspClient { request ); const res = new wasmclient.Results(); - if (result.body.Items) { + + if (result?.body !== null && result.body.Items) { for (const item of result.body.Items) { const key = Buffer.from(item.Key, "base64").toString(); const value = Buffer.from(item.Value, "base64"); @@ -69,12 +70,12 @@ export class WaspClient { ); } - private async sendRequest( + private async sendRequest( verb: "put" | "post" | "get" | "delete", path: string, request?: T | undefined, - ): Promise> { - let response: U; + ): Promise> { + let response: U | null = null; let fetchResponse: Response; try { @@ -92,13 +93,15 @@ export class WaspClient { try { response = await fetchResponse.json(); } catch (err) { + const error = err as Error; if (!fetchResponse.ok) { const text = await fetchResponse.text(); - throw new Error(err.message + " --- " + text); + throw new Error(error.message + " --- " + text); } } } catch (err) { - throw new Error("sendRequest: " + err.message); + const error = err as Error; + throw new Error("sendRequest: " + error.message); } return {body: response, response: fetchResponse}; From a729c5ebeea6a17bced5322fe32ca73121bb8ae5 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Mon, 3 Jan 2022 02:52:16 +0100 Subject: [PATCH 034/120] Updated target to es2021 --- packages/vm/wasmlib/ts/wasmclient/tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/tsconfig.json b/packages/vm/wasmlib/ts/wasmclient/tsconfig.json index 1b47fa376c..6154e4f4be 100644 --- a/packages/vm/wasmlib/ts/wasmclient/tsconfig.json +++ b/packages/vm/wasmlib/ts/wasmclient/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "strict": true, - "lib": ["es2020", "esnext", "dom"], - "target": "es2020", + "lib": ["ES2021", "esnext", "dom"], + "target": "ES2021", "module": "esnext", "sourceMap": true, "allowSyntheticDefaultImports": true, From d3889ac0f54d6cabf5afc973481a74e2fcd4de4f Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Mon, 3 Jan 2022 02:57:53 +0100 Subject: [PATCH 035/120] Exported configuration class and interface --- packages/vm/wasmlib/ts/wasmclient/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vm/wasmlib/ts/wasmclient/index.ts b/packages/vm/wasmlib/ts/wasmclient/index.ts index ac8aaeb8a0..5747f4997b 100644 --- a/packages/vm/wasmlib/ts/wasmclient/index.ts +++ b/packages/vm/wasmlib/ts/wasmclient/index.ts @@ -11,3 +11,4 @@ export * from "./serviceclient"; export * from "./transfer"; export * from "./types"; export * from "./waspclient"; +export * from "./configuration"; From 4d1529f581b948c79a40481f42ae058e34eacf69 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sun, 2 Jan 2022 18:09:50 -0800 Subject: [PATCH 036/120] Refactored TS events --- .../ts/testwasmlibclient/events.ts | 21 ++++----- .../ts/testwasmlibclient/service.ts | 30 ++++++------ packages/vm/wasmlib/ts/wasmclient/event.ts | 47 +++++++++---------- .../generator/tsclienttemplates/events.go | 15 +++--- 4 files changed, 55 insertions(+), 58 deletions(-) diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts index 61921dd7e6..27954a3e51 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts @@ -5,21 +5,20 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -import * as wasmclient from "wasmclient" +import * as wasmclient from "./wasmclient" import * as app from "./testwasmlib" export const eventHandlers: wasmclient.EventHandlers = { - "testwasmlib.test": onTestWasmLibTestThunk, + "testwasmlib.test": msg => app.onTestWasmLibTest(new EventTest(msg)), }; export class EventTest extends wasmclient.Event { - public address: wasmclient.Address; - public name: wasmclient.String; -} - -function onTestWasmLibTestThunk(message: string[]) { - let e = new EventTest(message); - e.address = e.nextAddress(); - e.name = e.nextString(); - app.onTestWasmLibTest(e); + public readonly address: wasmclient.Address; + public readonly name: wasmclient.String; + + public constructor(msg: string[]) { + super(msg) + this.address = this.nextAddress(); + this.name = this.nextString(); + } } diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts index 306bdcdf68..94a673064a 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -5,7 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -import * as wasmclient from "wasmclient" +import * as wasmclient from "./wasmclient" import * as events from "./events" const ArgAddress = "address"; @@ -344,55 +344,55 @@ export class IotaBalanceResults extends wasmclient.ViewResults { export class TestWasmLibService extends wasmclient.Service { - constructor(cl: wasmclient.ServiceClient, chainID: string) { - super(cl, chainID, 0x89703a45, events.eventHandlers); + constructor(cl: wasmclient.ServiceClient) { + super(cl, 0x89703a45, events.eventHandlers); } public arrayClear(): ArrayClearFunc { - return new ArrayClearFunc(this); + return new ArrayClearFunc(this); } public arrayCreate(): ArrayCreateFunc { - return new ArrayCreateFunc(this); + return new ArrayCreateFunc(this); } public arraySet(): ArraySetFunc { - return new ArraySetFunc(this); + return new ArraySetFunc(this); } public paramTypes(): ParamTypesFunc { - return new ParamTypesFunc(this); + return new ParamTypesFunc(this); } public random(): RandomFunc { - return new RandomFunc(this); + return new RandomFunc(this); } public triggerEvent(): TriggerEventFunc { - return new TriggerEventFunc(this); + return new TriggerEventFunc(this); } public arrayLength(): ArrayLengthView { - return new ArrayLengthView(this); + return new ArrayLengthView(this); } public arrayValue(): ArrayValueView { - return new ArrayValueView(this); + return new ArrayValueView(this); } public blockRecord(): BlockRecordView { - return new BlockRecordView(this); + return new BlockRecordView(this); } public blockRecords(): BlockRecordsView { - return new BlockRecordsView(this); + return new BlockRecordsView(this); } public getRandom(): GetRandomView { - return new GetRandomView(this); + return new GetRandomView(this); } public iotaBalance(): IotaBalanceView { - return new IotaBalanceView(this); + return new IotaBalanceView(this); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/event.ts b/packages/vm/wasmlib/ts/wasmclient/event.ts index 56d19d832a..f23c08220f 100644 --- a/packages/vm/wasmlib/ts/wasmclient/event.ts +++ b/packages/vm/wasmlib/ts/wasmclient/event.ts @@ -5,89 +5,88 @@ import * as wasmclient from "./index" import {Base58} from "./crypto"; export class Event { - private index: wasmclient.Int32; - private message: string[]; + private index = 0; + private readonly msg: string[]; public readonly timestamp: wasmclient.Int32; - constructor(message: string[]) { - this.message = message; - this.index = 0; + protected constructor(msg: string[]) { + this.msg = msg; this.timestamp = Number(this.next()); } private next(): string { - return this.message[this.index++]; + return this.msg[this.index++]; } - nextAddress(): wasmclient.Address { + protected nextAddress(): wasmclient.Address { return this.next(); } - nextAgentID(): wasmclient.AgentID { + protected nextAgentID(): wasmclient.AgentID { return this.next(); } - nextBool(): wasmclient.Bool { + protected nextBool(): wasmclient.Bool { return this.next() != "0"; } - nextBytes(): wasmclient.Bytes { + protected nextBytes(): wasmclient.Bytes { return Base58.decode(this.next()); } - nextChainID(): wasmclient.ChainID { + protected nextChainID(): wasmclient.ChainID { return this.next(); } - nextColor(): wasmclient.Color { + protected nextColor(): wasmclient.Color { return this.next(); } - nextHash(): wasmclient.Hash { + protected nextHash(): wasmclient.Hash { return this.next(); } - nextHname(): wasmclient.Hname { + protected nextHname(): wasmclient.Hname { return Number(this.next()); } - nextInt8(): wasmclient.Int8 { + protected nextInt8(): wasmclient.Int8 { return Number(this.next()); } - nextInt16(): wasmclient.Int16 { + protected nextInt16(): wasmclient.Int16 { return Number(this.next()); } - nextInt32(): wasmclient.Int32 { + protected nextInt32(): wasmclient.Int32 { return Number(this.next()); } - nextInt64(): wasmclient.Int64 { + protected nextInt64(): wasmclient.Int64 { return BigInt(this.next()); } - nextRequestID(): wasmclient.RequestID { + protected nextRequestID(): wasmclient.RequestID { return this.next(); } - nextString(): string { + protected nextString(): string { return this.next(); } - nextUint8(): wasmclient.Uint8 { + protected nextUint8(): wasmclient.Uint8 { return Number(this.next()); } - nextUint16(): wasmclient.Uint16 { + protected nextUint16(): wasmclient.Uint16 { return Number(this.next()); } - nextUint32(): wasmclient.Uint32 { + protected nextUint32(): wasmclient.Uint32 { return Number(this.next()); } - nextUint64(): wasmclient.Uint64 { + protected nextUint64(): wasmclient.Uint64 { return BigInt(this.next()); } } diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go index fef1f40333..e52002e859 100644 --- a/tools/schema/generator/tsclienttemplates/events.go +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -13,27 +13,26 @@ $#each events eventClass `, // ******************************* "eventHandler": ` - "$package.$evtName": on$PkgName$EvtName$+Thunk, + "$package.$evtName": msg => app.on$PkgName$EvtName(new Event$EvtName(msg)), `, // ******************************* "eventClass": ` export class Event$EvtName extends wasmclient.Event { $#each event eventClassField -} - -function on$PkgName$EvtName$+Thunk(message: string[]) { - const e = new Event$EvtName(message); + + public constructor(msg: string[]) { + super(msg) $#each event eventHandlerField - app.on$PkgName$EvtName(e); + } } `, // ******************************* "eventClassField": ` - public $fldName: wasmclient.$FldType | undefined; + public readonly $fldName: wasmclient.$FldType; `, // ******************************* "eventHandlerField": ` - e.$fldName = e.next$FldType(); + this.$fldName = this.next$FldType(); `, } From 1e670bd4ae169d371b663126095295dae6ffe126 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sun, 2 Jan 2022 18:16:09 -0800 Subject: [PATCH 037/120] Rolled back to use 'wasmclient' instead of './wasmclient' --- contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts | 2 +- contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts | 2 +- tools/schema/generator/tsclienttemplates/events.go | 2 +- tools/schema/generator/tsclienttemplates/service.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts index 27954a3e51..74257fc34c 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts @@ -5,7 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -import * as wasmclient from "./wasmclient" +import * as wasmclient from "wasmclient" import * as app from "./testwasmlib" export const eventHandlers: wasmclient.EventHandlers = { diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts index 94a673064a..f8353bdd08 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -5,7 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -import * as wasmclient from "./wasmclient" +import * as wasmclient from "wasmclient" import * as events from "./events" const ArgAddress = "address"; diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go index e52002e859..8d259137fc 100644 --- a/tools/schema/generator/tsclienttemplates/events.go +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -3,7 +3,7 @@ package tsclienttemplates var eventsTs = map[string]string{ // ******************************* "events.ts": ` -import * as wasmclient from "./wasmclient" +import * as wasmclient from "wasmclient" import * as app from "./$package" export const eventHandlers: wasmclient.EventHandlers = { diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 92a3f95fb1..638431ee67 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -3,7 +3,7 @@ package tsclienttemplates var serviceTs = map[string]string{ // ******************************* "service.ts": ` -import * as wasmclient from "./wasmclient" +import * as wasmclient from "wasmclient" import * as events from "./events" $#each params constArg From fc5e4d3976f732ccd82453211a8f914db7f69cbf Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Mon, 3 Jan 2022 03:49:20 +0100 Subject: [PATCH 038/120] Fixed bug which caused requests to fail --- packages/vm/wasmlib/ts/wasmclient/waspclient.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index b83e43de99..744a2adfce 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -39,7 +39,7 @@ export class WaspClient { const request = {Request: args.toString("base64")}; const result = await this.sendRequest( "get", - "/chain/" + chainID + "/contract/ " + contractHName + "/callview/" + entryPoint, + `/chain/${chainID}/contract/${contractHName}/callview/${entryPoint}`, request ); const res = new wasmclient.Results(); @@ -58,7 +58,7 @@ export class WaspClient { const request = {Request: offLedgerRequest.toString("base64")}; await this.sendRequest( "post", - "/request/" + chainID, + `/request/${chainID}`, request, ); } @@ -66,7 +66,7 @@ export class WaspClient { public async waitRequest(chainID: string, reqID: wasmclient.RequestID): Promise { await this.sendRequest( "get", - "/chain/" + chainID + "/request/" + reqID + "/wait", + `/chain/${chainID}/request/${reqID}/wait`, ); } From b15d85872d45f631c15d1b57255760c828af3167 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Mon, 3 Jan 2022 04:15:28 +0100 Subject: [PATCH 039/120] Added error when no configuration is defined. --- packages/vm/wasmlib/ts/wasmclient/configuration.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vm/wasmlib/ts/wasmclient/configuration.ts b/packages/vm/wasmlib/ts/wasmclient/configuration.ts index eb5a7bc4a1..4f25142a2f 100644 --- a/packages/vm/wasmlib/ts/wasmclient/configuration.ts +++ b/packages/vm/wasmlib/ts/wasmclient/configuration.ts @@ -15,6 +15,8 @@ export class Configuration implements IConfiguration { chainId: string = ''; constructor(configuration: IConfiguration) { + if(!configuration) throw new Error("Configuration not defined"); + this.seed = configuration.seed; this.waspWebSocketUrl = configuration.waspWebSocketUrl; this.waspApiUrl = configuration.waspApiUrl; From 3eb51a9eab2f4e042e470b4ee68e4d4cb70b027c Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Mon, 3 Jan 2022 04:22:32 +0100 Subject: [PATCH 040/120] Added string replace for chain id when configuring wasp web socket. --- packages/vm/wasmlib/ts/wasmclient/service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index d3262f06d4..d2dc9ee9fe 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -61,6 +61,9 @@ export class Service { this.waspWebSocketUrl = this.serviceClient.configuration.waspWebSocketUrl; else this.waspWebSocketUrl = "ws://" + this.serviceClient.configuration.waspWebSocketUrl; + + this.waspWebSocketUrl = this.waspWebSocketUrl.replace("%chainId", this.serviceClient.configuration.chainId); + this.connectWebSocket(); } From c4a2e958b5b83c073c479718eb64438728b13e1a Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Mon, 3 Jan 2022 04:24:03 +0100 Subject: [PATCH 041/120] changed http method for fetch to support body in request. --- packages/vm/wasmlib/ts/wasmclient/waspclient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 744a2adfce..733d881d6f 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -38,7 +38,7 @@ export class WaspClient { public async callView(chainID: string, contractHName: string, entryPoint: string, args: Buffer): Promise { const request = {Request: args.toString("base64")}; const result = await this.sendRequest( - "get", + "post", `/chain/${chainID}/contract/${contractHName}/callview/${entryPoint}`, request ); From 0601a2b8590f295ab603a6d217d2ba10f3b6d6a8 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sun, 2 Jan 2022 20:07:59 -0800 Subject: [PATCH 042/120] Fixed incorrect Buffer usage --- packages/vm/wasmlib/ts/wasmclient/arguments.ts | 17 +++++++++-------- packages/vm/wasmlib/ts/wasmclient/service.ts | 13 +++++++------ packages/vm/wasmlib/ts/wasmclient/transfer.ts | 13 ++++++++----- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 569878254a..68065695fc 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -137,18 +137,19 @@ export class Arguments { } keys.sort((lhs, rhs) => lhs.localeCompare(rhs)); - let buf = Buffer.alloc(0); + let buf = Buffer.alloc(4); buf.writeUInt32LE(keys.length, 0); for (const key of keys) { const keyBuf = Buffer.from("-" + key); - buf.writeUInt16LE(keyBuf.length, buf.length); - buf = Buffer.concat([buf, keyBuf]); - + const keyLen = Buffer.alloc(2); + keyLen.writeUInt16LE(keyBuf.length, 0); const valBuf = this.args.get(key); - if(!valBuf) continue; - - buf.writeUInt32LE(valBuf.length, buf.length); - buf = Buffer.concat([buf, valBuf]); + if (!valBuf) { + throw new Error("Arguments.encode: missing value"); + } + const valLen = Buffer.alloc(4); + valLen.writeUInt32LE(valBuf.length, 0); + buf = Buffer.concat([buf, keyLen, keyBuf, valLen, valBuf]); } return buf; } diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index d2dc9ee9fe..8b48781d64 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -33,13 +33,14 @@ export class Service { public async postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): Promise { // get request essence ready for signing let essence = Base58.decode(this.serviceClient.configuration.chainId); - essence.writeUInt32LE(this.scHname, essence.length); - essence.writeUInt32LE(hFuncName, essence.length); - essence = Buffer.concat([essence, args.encode(), keyPair.publicKey]); - essence.writeBigUInt64LE(BigInt(performance.now()), essence.length); - essence = Buffer.concat([essence, transfer.encode()]); + const hNames = Buffer.alloc(8) + hNames.writeUInt32LE(this.scHname, 0); + hNames.writeUInt32LE(hFuncName, 4); + const nonce = Buffer.alloc(8) + nonce.writeBigUInt64LE(BigInt(performance.now()), 0); + essence = Buffer.concat([essence, hNames, args.encode(), keyPair.publicKey, nonce, transfer.encode()]); - let buf = Buffer.alloc(0); + let buf = Buffer.alloc(1); const requestTypeOffledger = 1; buf.writeUInt8(requestTypeOffledger, 0); buf = Buffer.concat([buf, essence, ED25519.privateSign(keyPair, essence)]); diff --git a/packages/vm/wasmlib/ts/wasmclient/transfer.ts b/packages/vm/wasmlib/ts/wasmclient/transfer.ts index f1bea48ef9..c8784c32e3 100644 --- a/packages/vm/wasmlib/ts/wasmclient/transfer.ts +++ b/packages/vm/wasmlib/ts/wasmclient/transfer.ts @@ -41,13 +41,16 @@ export class Transfer { } keys.sort((lhs, rhs) => lhs.compare(rhs)); - let buf = Buffer.alloc(0); + let buf = Buffer.alloc(4); buf.writeUInt32LE(keys.length, 0); for (const key of keys) { - buf = Buffer.concat([buf, key]); - const value = this.xfer.get(key); - if(value === undefined || value === null) continue; - buf.writeBigUInt64LE(value, buf.length); + const val = this.xfer.get(key); + if (!val) { + throw new Error("Transfer.encode: missing amount"); + } + const valBuf = Buffer.alloc(8); + valBuf.writeBigUInt64LE(val, 0); + buf = Buffer.concat([buf, key, valBuf]); } return buf; } From 7a63332a3b765b3e70f367ec81eaf338c99dc200 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sun, 2 Jan 2022 20:43:39 -0800 Subject: [PATCH 043/120] Several fixes --- .../ts/testwasmlibclient/events.ts | 2 +- .../ts/testwasmlibclient/service.ts | 84 +++++++++---------- .../vm/wasmlib/ts/wasmclient/crypto/base58.ts | 12 +-- packages/vm/wasmlib/ts/wasmclient/service.ts | 2 +- .../generator/tsclienttemplates/events.go | 2 +- .../generator/tsclienttemplates/service.go | 19 +---- 6 files changed, 53 insertions(+), 68 deletions(-) diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts index 74257fc34c..2e11655cef 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts @@ -9,7 +9,7 @@ import * as wasmclient from "wasmclient" import * as app from "./testwasmlib" export const eventHandlers: wasmclient.EventHandlers = { - "testwasmlib.test": msg => app.onTestWasmLibTest(new EventTest(msg)), + "testwasmlib.test": (msg: string[]) => app.onTestWasmLibTest(new EventTest(msg)), }; export class EventTest extends wasmclient.Event { diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts index f8353bdd08..1446631506 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -43,9 +43,9 @@ const ResValue = "value"; ///////////////////////////// arrayClear ///////////////////////////// export class ArrayClearFunc extends wasmclient.ClientFunc { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - name(v: string): void { + public name(v: string): void { this.args.setString(ArgName, v); } @@ -58,9 +58,9 @@ export class ArrayClearFunc extends wasmclient.ClientFunc { ///////////////////////////// arrayCreate ///////////////////////////// export class ArrayCreateFunc extends wasmclient.ClientFunc { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - name(v: string): void { + public name(v: string): void { this.args.setString(ArgName, v); } @@ -73,17 +73,17 @@ export class ArrayCreateFunc extends wasmclient.ClientFunc { ///////////////////////////// arraySet ///////////////////////////// export class ArraySetFunc extends wasmclient.ClientFunc { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - index(v: wasmclient.Int32): void { + public index(v: wasmclient.Int32): void { this.args.setInt32(ArgIndex, v); } - name(v: string): void { + public name(v: string): void { this.args.setString(ArgName, v); } - value(v: string): void { + public value(v: string): void { this.args.setString(ArgValue, v); } @@ -98,81 +98,81 @@ export class ArraySetFunc extends wasmclient.ClientFunc { ///////////////////////////// paramTypes ///////////////////////////// export class ParamTypesFunc extends wasmclient.ClientFunc { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - address(v: wasmclient.Address): void { + public address(v: wasmclient.Address): void { this.args.setAddress(ArgAddress, v); } - agentID(v: wasmclient.AgentID): void { + public agentID(v: wasmclient.AgentID): void { this.args.setAgentID(ArgAgentID, v); } - bool(v: boolean): void { + public bool(v: boolean): void { this.args.setBool(ArgBool, v); } - bytes(v: wasmclient.Bytes): void { + public bytes(v: wasmclient.Bytes): void { this.args.setBytes(ArgBytes, v); } - chainID(v: wasmclient.ChainID): void { + public chainID(v: wasmclient.ChainID): void { this.args.setChainID(ArgChainID, v); } - color(v: wasmclient.Color): void { + public color(v: wasmclient.Color): void { this.args.setColor(ArgColor, v); } - hash(v: wasmclient.Hash): void { + public hash(v: wasmclient.Hash): void { this.args.setHash(ArgHash, v); } - hname(v: wasmclient.Hname): void { + public hname(v: wasmclient.Hname): void { this.args.setHname(ArgHname, v); } - int16(v: wasmclient.Int16): void { + public int16(v: wasmclient.Int16): void { this.args.setInt16(ArgInt16, v); } - int32(v: wasmclient.Int32): void { + public int32(v: wasmclient.Int32): void { this.args.setInt32(ArgInt32, v); } - int64(v: wasmclient.Int64): void { + public int64(v: wasmclient.Int64): void { this.args.setInt64(ArgInt64, v); } - int8(v: wasmclient.Int8): void { + public int8(v: wasmclient.Int8): void { this.args.setInt8(ArgInt8, v); } - param(v: wasmclient.Bytes): void { + public param(v: wasmclient.Bytes): void { this.args.setBytes(ArgParam, v); } - requestID(v: wasmclient.RequestID): void { + public requestID(v: wasmclient.RequestID): void { this.args.setRequestID(ArgRequestID, v); } - string(v: string): void { + public string(v: string): void { this.args.setString(ArgString, v); } - uint16(v: wasmclient.Uint16): void { + public uint16(v: wasmclient.Uint16): void { this.args.setUint16(ArgUint16, v); } - uint32(v: wasmclient.Uint32): void { + public uint32(v: wasmclient.Uint32): void { this.args.setUint32(ArgUint32, v); } - uint64(v: wasmclient.Uint64): void { + public uint64(v: wasmclient.Uint64): void { this.args.setUint64(ArgUint64, v); } - uint8(v: wasmclient.Uint8): void { + public uint8(v: wasmclient.Uint8): void { this.args.setUint8(ArgUint8, v); } @@ -193,13 +193,13 @@ export class RandomFunc extends wasmclient.ClientFunc { ///////////////////////////// triggerEvent ///////////////////////////// export class TriggerEventFunc extends wasmclient.ClientFunc { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - address(v: wasmclient.Address): void { + public address(v: wasmclient.Address): void { this.args.setAddress(ArgAddress, v); } - name(v: string): void { + public name(v: string): void { this.args.setString(ArgName, v); } @@ -213,9 +213,9 @@ export class TriggerEventFunc extends wasmclient.ClientFunc { ///////////////////////////// arrayLength ///////////////////////////// export class ArrayLengthView extends wasmclient.ClientView { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - name(v: string): void { + public name(v: string): void { this.args.setString(ArgName, v); } @@ -235,13 +235,13 @@ export class ArrayLengthResults extends wasmclient.ViewResults { ///////////////////////////// arrayValue ///////////////////////////// export class ArrayValueView extends wasmclient.ClientView { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - index(v: wasmclient.Int32): void { + public index(v: wasmclient.Int32): void { this.args.setInt32(ArgIndex, v); } - name(v: string): void { + public name(v: string): void { this.args.setString(ArgName, v); } @@ -262,13 +262,13 @@ export class ArrayValueResults extends wasmclient.ViewResults { ///////////////////////////// blockRecord ///////////////////////////// export class BlockRecordView extends wasmclient.ClientView { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - blockIndex(v: wasmclient.Int32): void { + public blockIndex(v: wasmclient.Int32): void { this.args.setInt32(ArgBlockIndex, v); } - recordIndex(v: wasmclient.Int32): void { + public recordIndex(v: wasmclient.Int32): void { this.args.setInt32(ArgRecordIndex, v); } @@ -289,9 +289,9 @@ export class BlockRecordResults extends wasmclient.ViewResults { ///////////////////////////// blockRecords ///////////////////////////// export class BlockRecordsView extends wasmclient.ClientView { - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); - blockIndex(v: wasmclient.Int32): void { + public blockIndex(v: wasmclient.Int32): void { this.args.setInt32(ArgBlockIndex, v); } @@ -344,7 +344,7 @@ export class IotaBalanceResults extends wasmclient.ViewResults { export class TestWasmLibService extends wasmclient.Service { - constructor(cl: wasmclient.ServiceClient) { + public constructor(cl: wasmclient.ServiceClient) { super(cl, 0x89703a45, events.eventHandlers); } diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts index d81ed61df5..e8cb7c4b5d 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts @@ -61,24 +61,20 @@ export class Base58 { return Buffer.from(''); } Base58.buildMap(); - let i = 0; - let j; - let c; - let carry; const bytes = [0]; - i = 0; + let i = 0; while (i < encoded.length) { - c = encoded[i]; + let c = encoded[i]; if (!(c in Base58.ALPHABET_MAP)) { throw new Error(`Character '${c}' is not in the Base58 alphabet.`); } - j = 0; + let j = 0; while (j < bytes.length) { bytes[j] *= 58; j++; } bytes[0] += Base58.ALPHABET_MAP[c]; - carry = 0; + let carry = 0; j = 0; while (j < bytes.length) { bytes[j] += carry; diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 8b48781d64..3a30f87730 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -37,7 +37,7 @@ export class Service { hNames.writeUInt32LE(this.scHname, 0); hNames.writeUInt32LE(hFuncName, 4); const nonce = Buffer.alloc(8) - nonce.writeBigUInt64LE(BigInt(performance.now()), 0); + nonce.writeBigUInt64LE(BigInt(Math.trunc(performance.now())), 0); essence = Buffer.concat([essence, hNames, args.encode(), keyPair.publicKey, nonce, transfer.encode()]); let buf = Buffer.alloc(1); diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go index 8d259137fc..afaa4f1207 100644 --- a/tools/schema/generator/tsclienttemplates/events.go +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -13,7 +13,7 @@ $#each events eventClass `, // ******************************* "eventHandler": ` - "$package.$evtName": msg => app.on$PkgName$EvtName(new Event$EvtName(msg)), + "$package.$evtName": (msg: string[]) => app.on$PkgName$EvtName(new Event$EvtName(msg)), `, // ******************************* "eventClass": ` diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 638431ee67..3a74d2b141 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -15,7 +15,7 @@ $#each func funcStruct export class $PkgName$+Service extends wasmclient.Service { - constructor(cl: wasmclient.ServiceClient) { + public constructor(cl: wasmclient.ServiceClient) { super(cl, 0x$hscName, events.eventHandlers); } $#each func serviceFunction @@ -43,7 +43,7 @@ $#if result resultStruct `, // ******************************* "funcArgsMember": ` - args: wasmclient.Arguments = new wasmclient.Arguments(); + private args: wasmclient.Arguments = new wasmclient.Arguments(); `, // ******************************* "funcArgSetter": ` @@ -52,14 +52,14 @@ $#if array funcArgSetterArray funcArgSetterBasic // ******************************* "funcArgSetterBasic": ` - $fldName(v: $fldLangType): void { + public $fldName(v: $fldLangType): void { this.args.set$FldType(Arg$FldName, v); } `, // ******************************* "funcArgSetterArray": ` - $fldName(a: $fldLangType[]): void { + public $fldName(a: $fldLangType[]): void { for (let i = 0; i < a.length; i++) { this.args.set$FldType(this.args.indexedKey(Arg$FldName, i), a[i]); } @@ -117,17 +117,6 @@ $#if mandatory else callResultOptional $fldName$+Exists(): boolean { return this.res.exists(Res$FldName) } -`, - // ******************************* - "serviceResultExtract": ` - let $fldName = result["$fldName"]; - if ($fldName) { - this.$fldName = $fldName.$resConvert; - } -`, - // ******************************* - "serviceResult": ` - $fldName: wasmclient.$FldType; `, // ******************************* "serviceFunction": ` From 6338434c4bd37eb85b37de99a281695d823932fc Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Tue, 4 Jan 2022 00:58:11 +0100 Subject: [PATCH 044/120] Fix build error on strict compilation --- packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts index e8cb7c4b5d..fdb2dd5943 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/base58.ts @@ -64,7 +64,7 @@ export class Base58 { const bytes = [0]; let i = 0; while (i < encoded.length) { - let c = encoded[i]; + const c = encoded[i]; if (!(c in Base58.ALPHABET_MAP)) { throw new Error(`Character '${c}' is not in the Base58 alphabet.`); } From 473e47fec10998dd4e96b4b2fe5c24852d8c21e6 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Tue, 4 Jan 2022 03:31:32 +0100 Subject: [PATCH 045/120] Fixed bug in subseed generation logic which caused failure to generate key pairs. --- packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts index 0c2330fc9b..f964f6cdb3 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts @@ -1,7 +1,7 @@ -import {Base58} from './base58'; -import {Buffer} from '../buffer'; -import {ED25519, IKeyPair} from './ed25519'; -import {Hash} from "./hash"; +import { Base58 } from './base58'; +import { Buffer } from '../buffer'; +import { ED25519, IKeyPair } from './ed25519'; +import { Hash } from './hash'; export class Seed { /** @@ -32,7 +32,9 @@ export class Seed { const indexBytes = Buffer.alloc(8); indexBytes.writeBigUInt64LE(BigInt(index), 0); const subseed = Buffer.alloc(this.SEED_SIZE); - Seed.xorBytes(seed, Hash.from(indexBytes), subseed); + const hashOfPaddedBuffer = Hash.from(indexBytes); + + Seed.xorBytes(seed, Buffer.from(hashOfPaddedBuffer), subseed); return subseed; } From 75f09cd391d38fc53b9f991c5e70d704384344ca Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Tue, 4 Jan 2022 03:34:05 +0100 Subject: [PATCH 046/120] fixed bug which caused address generation to fail --- packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts index f964f6cdb3..8ea5b3753e 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts @@ -77,7 +77,7 @@ export class Seed { const buffer = Buffer.alloc(Seed.SEED_SIZE + 1); buffer[0] = ED25519.VERSION; - digest.copy(buffer, 1); + Buffer.from(digest).copy(buffer, 1); return Base58.encode(buffer); } From 29d8de21325cbe04a8a6cfc1e9eaa814bd15f508 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Wed, 5 Jan 2022 01:38:34 +0100 Subject: [PATCH 047/120] Added colors to wasmclient --- packages/vm/wasmlib/ts/wasmclient/colors.ts | 7 +++++++ packages/vm/wasmlib/ts/wasmclient/index.ts | 1 + 2 files changed, 8 insertions(+) create mode 100644 packages/vm/wasmlib/ts/wasmclient/colors.ts diff --git a/packages/vm/wasmlib/ts/wasmclient/colors.ts b/packages/vm/wasmlib/ts/wasmclient/colors.ts new file mode 100644 index 0000000000..2d5ee90ba8 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/colors.ts @@ -0,0 +1,7 @@ +import { Buffer } from './buffer'; + +export class Colors { + public static readonly IOTA_COLOR_STRING: string = '11111111111111111111111111111111'; + public static readonly IOTA_COLOR_BYTES: Buffer = Buffer.alloc(32); +} +export type ColorCollection = { [key: string]: bigint }; diff --git a/packages/vm/wasmlib/ts/wasmclient/index.ts b/packages/vm/wasmlib/ts/wasmclient/index.ts index 5747f4997b..62f1d2eadd 100644 --- a/packages/vm/wasmlib/ts/wasmclient/index.ts +++ b/packages/vm/wasmlib/ts/wasmclient/index.ts @@ -12,3 +12,4 @@ export * from "./transfer"; export * from "./types"; export * from "./waspclient"; export * from "./configuration"; +export * from "./colors"; From fbbc1511e591de174685e7683ea36ae08dd60a76 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Wed, 5 Jan 2022 02:14:17 +0100 Subject: [PATCH 048/120] Removed colors --- packages/vm/wasmlib/ts/wasmclient/colors.ts | 7 ------- packages/vm/wasmlib/ts/wasmclient/index.ts | 1 - 2 files changed, 8 deletions(-) delete mode 100644 packages/vm/wasmlib/ts/wasmclient/colors.ts diff --git a/packages/vm/wasmlib/ts/wasmclient/colors.ts b/packages/vm/wasmlib/ts/wasmclient/colors.ts deleted file mode 100644 index 2d5ee90ba8..0000000000 --- a/packages/vm/wasmlib/ts/wasmclient/colors.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Buffer } from './buffer'; - -export class Colors { - public static readonly IOTA_COLOR_STRING: string = '11111111111111111111111111111111'; - public static readonly IOTA_COLOR_BYTES: Buffer = Buffer.alloc(32); -} -export type ColorCollection = { [key: string]: bigint }; diff --git a/packages/vm/wasmlib/ts/wasmclient/index.ts b/packages/vm/wasmlib/ts/wasmclient/index.ts index 62f1d2eadd..5747f4997b 100644 --- a/packages/vm/wasmlib/ts/wasmclient/index.ts +++ b/packages/vm/wasmlib/ts/wasmclient/index.ts @@ -12,4 +12,3 @@ export * from "./transfer"; export * from "./types"; export * from "./waspclient"; export * from "./configuration"; -export * from "./colors"; From 2a511d41f3489c2e47ff3faf8caf56a7a4a264cc Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Tue, 4 Jan 2022 18:17:12 -0800 Subject: [PATCH 049/120] Fixed Rust structs to add cloning ability --- contracts/wasm/dividend/src/state.rs | 4 + .../wasm/donatewithfeedback/src/state.rs | 2 + .../wasm/donatewithfeedback/src/structs.rs | 3 + contracts/wasm/erc20/src/state.rs | 2 + contracts/wasm/erc20/src/typedefs.rs | 2 + contracts/wasm/erc721/src/state.rs | 6 + contracts/wasm/erc721/src/typedefs.rs | 2 + contracts/wasm/fairauction/src/state.rs | 6 + contracts/wasm/fairauction/src/structs.rs | 6 + contracts/wasm/fairauction/src/typedefs.rs | 4 + contracts/wasm/fairroulette/src/state.rs | 2 + contracts/wasm/fairroulette/src/structs.rs | 3 + contracts/wasm/testcore/src/results.rs | 4 + .../go/testwasmlibclient/events.go | 28 -- .../go/testwasmlibclient/service.go | 428 ------------------ .../go/testwasmlibclient/testwasmlib.go | 7 - contracts/wasm/testwasmlib/schema.yaml | 1 + contracts/wasm/testwasmlib/src/params.rs | 2 + contracts/wasm/testwasmlib/src/state.rs | 2 + contracts/wasm/testwasmlib/src/typedefs.rs | 2 + .../wasm/testwasmlib/test/testwasmlib_test.go | 108 ----- .../ts/testwasmlibclient/events.ts | 24 - .../testwasmlib/ts/testwasmlibclient/index.ts | 10 - .../ts/testwasmlibclient/service.ts | 398 ---------------- .../ts/testwasmlibclient/testwasmlib.ts | 8 - .../ts/testwasmlibclient/tsconfig.json | 11 - contracts/wasm/tokenregistry/src/state.rs | 4 + contracts/wasm/tokenregistry/src/structs.rs | 3 + .../vm/wasmlib/src/coreaccounts/results.rs | 4 + packages/vm/wasmlib/src/coreblob/params.rs | 2 + packages/vm/wasmlib/src/coreblob/results.rs | 4 + .../vm/wasmlib/src/coreblocklog/results.rs | 4 + .../vm/wasmlib/src/coregovernance/results.rs | 2 + packages/vm/wasmlib/src/coreroot/results.rs | 2 + packages/vm/wasmlib/ts/wasmclient/service.ts | 24 +- .../vm/wasmlib/ts/wasmclient/serviceclient.ts | 8 +- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 42 +- tools/schema/generator/rstemplates/structs.go | 2 + .../schema/generator/rstemplates/typedefs.go | 2 + 39 files changed, 140 insertions(+), 1038 deletions(-) delete mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/events.go delete mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/service.go delete mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go delete mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts delete mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts delete mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts delete mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts delete mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json diff --git a/contracts/wasm/dividend/src/state.rs b/contracts/wasm/dividend/src/state.rs index 1181e0fd48..351caa28fa 100644 --- a/contracts/wasm/dividend/src/state.rs +++ b/contracts/wasm/dividend/src/state.rs @@ -14,6 +14,7 @@ use wasmlib::host::*; use crate::*; use crate::keys::*; +#[derive(Clone, Copy)] pub struct ArrayOfImmutableAddress { pub(crate) obj_id: i32, } @@ -28,6 +29,7 @@ impl ArrayOfImmutableAddress { } } +#[derive(Clone, Copy)] pub struct MapAddressToImmutableInt64 { pub(crate) obj_id: i32, } @@ -63,6 +65,7 @@ impl ImmutableDividendState { } } +#[derive(Clone, Copy)] pub struct ArrayOfMutableAddress { pub(crate) obj_id: i32, } @@ -81,6 +84,7 @@ impl ArrayOfMutableAddress { } } +#[derive(Clone, Copy)] pub struct MapAddressToMutableInt64 { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/donatewithfeedback/src/state.rs b/contracts/wasm/donatewithfeedback/src/state.rs index 9fc1c32e0f..d7ef3fcf92 100644 --- a/contracts/wasm/donatewithfeedback/src/state.rs +++ b/contracts/wasm/donatewithfeedback/src/state.rs @@ -15,6 +15,7 @@ use crate::*; use crate::keys::*; use crate::structs::*; +#[derive(Clone, Copy)] pub struct ArrayOfImmutableDonation { pub(crate) obj_id: i32, } @@ -49,6 +50,7 @@ impl ImmutableDonateWithFeedbackState { } } +#[derive(Clone, Copy)] pub struct ArrayOfMutableDonation { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/donatewithfeedback/src/structs.rs b/contracts/wasm/donatewithfeedback/src/structs.rs index 8a15175075..b1ebbda1da 100644 --- a/contracts/wasm/donatewithfeedback/src/structs.rs +++ b/contracts/wasm/donatewithfeedback/src/structs.rs @@ -11,6 +11,7 @@ use wasmlib::*; use wasmlib::host::*; +#[derive(Clone)] pub struct Donation { pub amount : i64, // amount donated pub donator : ScAgentID, // who donated @@ -42,6 +43,7 @@ impl Donation { } } +#[derive(Clone, Copy)] pub struct ImmutableDonation { pub(crate) obj_id: i32, pub(crate) key_id: Key32, @@ -57,6 +59,7 @@ impl ImmutableDonation { } } +#[derive(Clone, Copy)] pub struct MutableDonation { pub(crate) obj_id: i32, pub(crate) key_id: Key32, diff --git a/contracts/wasm/erc20/src/state.rs b/contracts/wasm/erc20/src/state.rs index c67adb6537..2549d40de5 100644 --- a/contracts/wasm/erc20/src/state.rs +++ b/contracts/wasm/erc20/src/state.rs @@ -15,6 +15,7 @@ use crate::*; use crate::keys::*; use crate::typedefs::*; +#[derive(Clone, Copy)] pub struct MapAgentIDToImmutableAllowancesForAgent { pub(crate) obj_id: i32, } @@ -47,6 +48,7 @@ impl ImmutableErc20State { } } +#[derive(Clone, Copy)] pub struct MapAgentIDToMutableAllowancesForAgent { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/erc20/src/typedefs.rs b/contracts/wasm/erc20/src/typedefs.rs index 530075de78..dc638d395a 100644 --- a/contracts/wasm/erc20/src/typedefs.rs +++ b/contracts/wasm/erc20/src/typedefs.rs @@ -10,6 +10,7 @@ use wasmlib::*; use wasmlib::host::*; +#[derive(Clone, Copy)] pub struct MapAgentIDToImmutableInt64 { pub(crate) obj_id: i32, } @@ -22,6 +23,7 @@ impl MapAgentIDToImmutableInt64 { pub type ImmutableAllowancesForAgent = MapAgentIDToImmutableInt64; +#[derive(Clone, Copy)] pub struct MapAgentIDToMutableInt64 { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/erc721/src/state.rs b/contracts/wasm/erc721/src/state.rs index 134d226129..ae8cd1e68a 100644 --- a/contracts/wasm/erc721/src/state.rs +++ b/contracts/wasm/erc721/src/state.rs @@ -15,6 +15,7 @@ use crate::*; use crate::keys::*; use crate::typedefs::*; +#[derive(Clone, Copy)] pub struct MapHashToImmutableAgentID { pub(crate) obj_id: i32, } @@ -25,6 +26,7 @@ impl MapHashToImmutableAgentID { } } +#[derive(Clone, Copy)] pub struct MapAgentIDToImmutableOperators { pub(crate) obj_id: i32, } @@ -36,6 +38,7 @@ impl MapAgentIDToImmutableOperators { } } +#[derive(Clone, Copy)] pub struct MapAgentIDToImmutableUint64 { pub(crate) obj_id: i32, } @@ -81,6 +84,7 @@ impl ImmutableErc721State { } } +#[derive(Clone, Copy)] pub struct MapHashToMutableAgentID { pub(crate) obj_id: i32, } @@ -95,6 +99,7 @@ impl MapHashToMutableAgentID { } } +#[derive(Clone, Copy)] pub struct MapAgentIDToMutableOperators { pub(crate) obj_id: i32, } @@ -110,6 +115,7 @@ impl MapAgentIDToMutableOperators { } } +#[derive(Clone, Copy)] pub struct MapAgentIDToMutableUint64 { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/erc721/src/typedefs.rs b/contracts/wasm/erc721/src/typedefs.rs index 33eacd20c5..63cd1a30d9 100644 --- a/contracts/wasm/erc721/src/typedefs.rs +++ b/contracts/wasm/erc721/src/typedefs.rs @@ -10,6 +10,7 @@ use wasmlib::*; use wasmlib::host::*; +#[derive(Clone, Copy)] pub struct MapAgentIDToImmutableBool { pub(crate) obj_id: i32, } @@ -22,6 +23,7 @@ impl MapAgentIDToImmutableBool { pub type ImmutableOperators = MapAgentIDToImmutableBool; +#[derive(Clone, Copy)] pub struct MapAgentIDToMutableBool { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/fairauction/src/state.rs b/contracts/wasm/fairauction/src/state.rs index aeff8bb397..8e35dbc21b 100644 --- a/contracts/wasm/fairauction/src/state.rs +++ b/contracts/wasm/fairauction/src/state.rs @@ -16,6 +16,7 @@ use crate::keys::*; use crate::structs::*; use crate::typedefs::*; +#[derive(Clone, Copy)] pub struct MapColorToImmutableAuction { pub(crate) obj_id: i32, } @@ -26,6 +27,7 @@ impl MapColorToImmutableAuction { } } +#[derive(Clone, Copy)] pub struct MapColorToImmutableBidderList { pub(crate) obj_id: i32, } @@ -37,6 +39,7 @@ impl MapColorToImmutableBidderList { } } +#[derive(Clone, Copy)] pub struct MapColorToImmutableBids { pub(crate) obj_id: i32, } @@ -74,6 +77,7 @@ impl ImmutableFairAuctionState { } } +#[derive(Clone, Copy)] pub struct MapColorToMutableAuction { pub(crate) obj_id: i32, } @@ -88,6 +92,7 @@ impl MapColorToMutableAuction { } } +#[derive(Clone, Copy)] pub struct MapColorToMutableBidderList { pub(crate) obj_id: i32, } @@ -103,6 +108,7 @@ impl MapColorToMutableBidderList { } } +#[derive(Clone, Copy)] pub struct MapColorToMutableBids { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/fairauction/src/structs.rs b/contracts/wasm/fairauction/src/structs.rs index a59feab046..8a0f7e7194 100644 --- a/contracts/wasm/fairauction/src/structs.rs +++ b/contracts/wasm/fairauction/src/structs.rs @@ -12,6 +12,7 @@ use wasmlib::*; use wasmlib::host::*; use crate::typedefs::*; +#[derive(Clone)] pub struct Auction { pub color : ScColor, // color of tokens for sale pub creator : ScAgentID, // issuer of start_auction transaction @@ -61,6 +62,7 @@ impl Auction { } } +#[derive(Clone, Copy)] pub struct ImmutableAuction { pub(crate) obj_id: i32, pub(crate) key_id: Key32, @@ -76,6 +78,7 @@ impl ImmutableAuction { } } +#[derive(Clone, Copy)] pub struct MutableAuction { pub(crate) obj_id: i32, pub(crate) key_id: Key32, @@ -99,6 +102,7 @@ impl MutableAuction { } } +#[derive(Clone)] pub struct Bid { pub amount : i64, // cumulative amount of bids from same bidder pub index : i32, // index of bidder in bidder list @@ -124,6 +128,7 @@ impl Bid { } } +#[derive(Clone, Copy)] pub struct ImmutableBid { pub(crate) obj_id: i32, pub(crate) key_id: Key32, @@ -139,6 +144,7 @@ impl ImmutableBid { } } +#[derive(Clone, Copy)] pub struct MutableBid { pub(crate) obj_id: i32, pub(crate) key_id: Key32, diff --git a/contracts/wasm/fairauction/src/typedefs.rs b/contracts/wasm/fairauction/src/typedefs.rs index dc306d8ad5..83938d4385 100644 --- a/contracts/wasm/fairauction/src/typedefs.rs +++ b/contracts/wasm/fairauction/src/typedefs.rs @@ -11,6 +11,7 @@ use wasmlib::*; use wasmlib::host::*; use crate::structs::*; +#[derive(Clone, Copy)] pub struct ArrayOfImmutableAgentID { pub(crate) obj_id: i32, } @@ -27,6 +28,7 @@ impl ArrayOfImmutableAgentID { pub type ImmutableBidderList = ArrayOfImmutableAgentID; +#[derive(Clone, Copy)] pub struct ArrayOfMutableAgentID { pub(crate) obj_id: i32, } @@ -47,6 +49,7 @@ impl ArrayOfMutableAgentID { pub type MutableBidderList = ArrayOfMutableAgentID; +#[derive(Clone, Copy)] pub struct MapAgentIDToImmutableBid { pub(crate) obj_id: i32, } @@ -59,6 +62,7 @@ impl MapAgentIDToImmutableBid { pub type ImmutableBids = MapAgentIDToImmutableBid; +#[derive(Clone, Copy)] pub struct MapAgentIDToMutableBid { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/fairroulette/src/state.rs b/contracts/wasm/fairroulette/src/state.rs index 7277b6f457..a9dab54565 100644 --- a/contracts/wasm/fairroulette/src/state.rs +++ b/contracts/wasm/fairroulette/src/state.rs @@ -15,6 +15,7 @@ use crate::*; use crate::keys::*; use crate::structs::*; +#[derive(Clone, Copy)] pub struct ArrayOfImmutableBet { pub(crate) obj_id: i32, } @@ -61,6 +62,7 @@ impl ImmutableFairRouletteState { } } +#[derive(Clone, Copy)] pub struct ArrayOfMutableBet { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/fairroulette/src/structs.rs b/contracts/wasm/fairroulette/src/structs.rs index 501d719fbf..4bd7f082bc 100644 --- a/contracts/wasm/fairroulette/src/structs.rs +++ b/contracts/wasm/fairroulette/src/structs.rs @@ -11,6 +11,7 @@ use wasmlib::*; use wasmlib::host::*; +#[derive(Clone)] pub struct Bet { pub amount : i64, pub better : ScAgentID, @@ -36,6 +37,7 @@ impl Bet { } } +#[derive(Clone, Copy)] pub struct ImmutableBet { pub(crate) obj_id: i32, pub(crate) key_id: Key32, @@ -51,6 +53,7 @@ impl ImmutableBet { } } +#[derive(Clone, Copy)] pub struct MutableBet { pub(crate) obj_id: i32, pub(crate) key_id: Key32, diff --git a/contracts/wasm/testcore/src/results.rs b/contracts/wasm/testcore/src/results.rs index 974409c2dc..0971eb7005 100644 --- a/contracts/wasm/testcore/src/results.rs +++ b/contracts/wasm/testcore/src/results.rs @@ -154,6 +154,7 @@ impl MutableGetCounterResults { } } +#[derive(Clone, Copy)] pub struct MapStringToImmutableInt64 { pub(crate) obj_id: i32, } @@ -175,6 +176,7 @@ impl ImmutableGetIntResults { } } +#[derive(Clone, Copy)] pub struct MapStringToMutableInt64 { pub(crate) obj_id: i32, } @@ -200,6 +202,7 @@ impl MutableGetIntResults { } } +#[derive(Clone, Copy)] pub struct MapStringToImmutableString { pub(crate) obj_id: i32, } @@ -221,6 +224,7 @@ impl ImmutableGetStringValueResults { } } +#[derive(Clone, Copy)] pub struct MapStringToMutableString { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go deleted file mode 100644 index e0c3faf93c..0000000000 --- a/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -package testwasmlibclient - -import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" - -var EventHandlers = map[string]func([]string){ - "testwasmlib.test": onTestWasmLibTestThunk, -} - -type EventTest struct { - wasmclient.Event - Address wasmclient.Address - Name string -} - -func onTestWasmLibTestThunk(message []string) { - e := &EventTest{} - e.Init(message) - e.Address = e.NextAddress() - e.Name = e.NextString() - OnTestWasmLibTest(e) -} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go deleted file mode 100644 index 44566db07b..0000000000 --- a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -package testwasmlibclient - -import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" - -const ( - ArgAddress = "address" - ArgAgentID = "agentID" - ArgBlockIndex = "blockIndex" - ArgBool = "bool" - ArgBytes = "bytes" - ArgChainID = "chainID" - ArgColor = "color" - ArgHash = "hash" - ArgHname = "hname" - ArgIndex = "index" - ArgInt16 = "int16" - ArgInt32 = "int32" - ArgInt64 = "int64" - ArgInt8 = "int8" - ArgName = "name" - ArgParam = "this" - ArgRecordIndex = "recordIndex" - ArgRequestID = "requestID" - ArgString = "string" - ArgUint16 = "uint16" - ArgUint32 = "uint32" - ArgUint64 = "uint64" - ArgUint8 = "uint8" - ArgValue = "value" - - ResCount = "count" - ResIotas = "iotas" - ResLength = "length" - ResRandom = "random" - ResRecord = "record" - ResValue = "value" -) - -///////////////////////////// arrayClear ///////////////////////////// - -type ArrayClearFunc struct { - wasmclient.ClientFunc - args wasmclient.Arguments -} - -func (f *ArrayClearFunc) Name(v string) { - f.args.SetString(ArgName, v) -} - -func (f *ArrayClearFunc) Post() wasmclient.Request { - f.args.Mandatory(ArgName) - return f.ClientFunc.Post(0x88021821, &f.args) -} - -///////////////////////////// arrayCreate ///////////////////////////// - -type ArrayCreateFunc struct { - wasmclient.ClientFunc - args wasmclient.Arguments -} - -func (f *ArrayCreateFunc) Name(v string) { - f.args.SetString(ArgName, v) -} - -func (f *ArrayCreateFunc) Post() wasmclient.Request { - f.args.Mandatory(ArgName) - return f.ClientFunc.Post(0x1ed5b23b, &f.args) -} - -///////////////////////////// arraySet ///////////////////////////// - -type ArraySetFunc struct { - wasmclient.ClientFunc - args wasmclient.Arguments -} - -func (f *ArraySetFunc) Index(v int32) { - f.args.SetInt32(ArgIndex, v) -} - -func (f *ArraySetFunc) Name(v string) { - f.args.SetString(ArgName, v) -} - -func (f *ArraySetFunc) Value(v string) { - f.args.SetString(ArgValue, v) -} - -func (f *ArraySetFunc) Post() wasmclient.Request { - f.args.Mandatory(ArgIndex) - f.args.Mandatory(ArgName) - f.args.Mandatory(ArgValue) - return f.ClientFunc.Post(0x2c4150b3, &f.args) -} - -///////////////////////////// paramTypes ///////////////////////////// - -type ParamTypesFunc struct { - wasmclient.ClientFunc - args wasmclient.Arguments -} - -func (f *ParamTypesFunc) Address(v wasmclient.Address) { - f.args.SetAddress(ArgAddress, v) -} - -func (f *ParamTypesFunc) AgentID(v wasmclient.AgentID) { - f.args.SetAgentID(ArgAgentID, v) -} - -func (f *ParamTypesFunc) Bool(v bool) { - f.args.SetBool(ArgBool, v) -} - -func (f *ParamTypesFunc) Bytes(v []byte) { - f.args.SetBytes(ArgBytes, v) -} - -func (f *ParamTypesFunc) ChainID(v wasmclient.ChainID) { - f.args.SetChainID(ArgChainID, v) -} - -func (f *ParamTypesFunc) Color(v wasmclient.Color) { - f.args.SetColor(ArgColor, v) -} - -func (f *ParamTypesFunc) Hash(v wasmclient.Hash) { - f.args.SetHash(ArgHash, v) -} - -func (f *ParamTypesFunc) Hname(v wasmclient.Hname) { - f.args.SetHname(ArgHname, v) -} - -func (f *ParamTypesFunc) Int16(v int16) { - f.args.SetInt16(ArgInt16, v) -} - -func (f *ParamTypesFunc) Int32(v int32) { - f.args.SetInt32(ArgInt32, v) -} - -func (f *ParamTypesFunc) Int64(v int64) { - f.args.SetInt64(ArgInt64, v) -} - -func (f *ParamTypesFunc) Int8(v int8) { - f.args.SetInt8(ArgInt8, v) -} - -func (f *ParamTypesFunc) Param(v []byte) { - f.args.SetBytes(ArgParam, v) -} - -func (f *ParamTypesFunc) RequestID(v wasmclient.RequestID) { - f.args.SetRequestID(ArgRequestID, v) -} - -func (f *ParamTypesFunc) String(v string) { - f.args.SetString(ArgString, v) -} - -func (f *ParamTypesFunc) Uint16(v uint16) { - f.args.SetUint16(ArgUint16, v) -} - -func (f *ParamTypesFunc) Uint32(v uint32) { - f.args.SetUint32(ArgUint32, v) -} - -func (f *ParamTypesFunc) Uint64(v uint64) { - f.args.SetUint64(ArgUint64, v) -} - -func (f *ParamTypesFunc) Uint8(v uint8) { - f.args.SetUint8(ArgUint8, v) -} - -func (f *ParamTypesFunc) Post() wasmclient.Request { - return f.ClientFunc.Post(0x6921c4cd, &f.args) -} - -///////////////////////////// random ///////////////////////////// - -type RandomFunc struct { - wasmclient.ClientFunc -} - -func (f *RandomFunc) Post() wasmclient.Request { - return f.ClientFunc.Post(0xe86c97ca, nil) -} - -///////////////////////////// triggerEvent ///////////////////////////// - -type TriggerEventFunc struct { - wasmclient.ClientFunc - args wasmclient.Arguments -} - -func (f *TriggerEventFunc) Address(v wasmclient.Address) { - f.args.SetAddress(ArgAddress, v) -} - -func (f *TriggerEventFunc) Name(v string) { - f.args.SetString(ArgName, v) -} - -func (f *TriggerEventFunc) Post() wasmclient.Request { - f.args.Mandatory(ArgAddress) - f.args.Mandatory(ArgName) - return f.ClientFunc.Post(0xd5438ac6, &f.args) -} - -///////////////////////////// arrayLength ///////////////////////////// - -type ArrayLengthView struct { - wasmclient.ClientView - args wasmclient.Arguments -} - -func (f *ArrayLengthView) Name(v string) { - f.args.SetString(ArgName, v) -} - -func (f *ArrayLengthView) Call() ArrayLengthResults { - f.args.Mandatory(ArgName) - f.ClientView.Call("arrayLength", &f.args) - return ArrayLengthResults{res: f.Results()} -} - -type ArrayLengthResults struct { - res wasmclient.Results -} - -func (r *ArrayLengthResults) Length() int32 { - return r.res.GetInt32(ResLength) -} - -///////////////////////////// arrayValue ///////////////////////////// - -type ArrayValueView struct { - wasmclient.ClientView - args wasmclient.Arguments -} - -func (f *ArrayValueView) Index(v int32) { - f.args.SetInt32(ArgIndex, v) -} - -func (f *ArrayValueView) Name(v string) { - f.args.SetString(ArgName, v) -} - -func (f *ArrayValueView) Call() ArrayValueResults { - f.args.Mandatory(ArgIndex) - f.args.Mandatory(ArgName) - f.ClientView.Call("arrayValue", &f.args) - return ArrayValueResults{res: f.Results()} -} - -type ArrayValueResults struct { - res wasmclient.Results -} - -func (r *ArrayValueResults) Value() string { - return r.res.GetString(ResValue) -} - -///////////////////////////// blockRecord ///////////////////////////// - -type BlockRecordView struct { - wasmclient.ClientView - args wasmclient.Arguments -} - -func (f *BlockRecordView) BlockIndex(v int32) { - f.args.SetInt32(ArgBlockIndex, v) -} - -func (f *BlockRecordView) RecordIndex(v int32) { - f.args.SetInt32(ArgRecordIndex, v) -} - -func (f *BlockRecordView) Call() BlockRecordResults { - f.args.Mandatory(ArgBlockIndex) - f.args.Mandatory(ArgRecordIndex) - f.ClientView.Call("blockRecord", &f.args) - return BlockRecordResults{res: f.Results()} -} - -type BlockRecordResults struct { - res wasmclient.Results -} - -func (r *BlockRecordResults) Record() []byte { - return r.res.GetBytes(ResRecord) -} - -///////////////////////////// blockRecords ///////////////////////////// - -type BlockRecordsView struct { - wasmclient.ClientView - args wasmclient.Arguments -} - -func (f *BlockRecordsView) BlockIndex(v int32) { - f.args.SetInt32(ArgBlockIndex, v) -} - -func (f *BlockRecordsView) Call() BlockRecordsResults { - f.args.Mandatory(ArgBlockIndex) - f.ClientView.Call("blockRecords", &f.args) - return BlockRecordsResults{res: f.Results()} -} - -type BlockRecordsResults struct { - res wasmclient.Results -} - -func (r *BlockRecordsResults) Count() int32 { - return r.res.GetInt32(ResCount) -} - -///////////////////////////// getRandom ///////////////////////////// - -type GetRandomView struct { - wasmclient.ClientView -} - -func (f *GetRandomView) Call() GetRandomResults { - f.ClientView.Call("getRandom", nil) - return GetRandomResults{res: f.Results()} -} - -type GetRandomResults struct { - res wasmclient.Results -} - -func (r *GetRandomResults) Random() int64 { - return r.res.GetInt64(ResRandom) -} - -///////////////////////////// iotaBalance ///////////////////////////// - -type IotaBalanceView struct { - wasmclient.ClientView -} - -func (f *IotaBalanceView) Call() IotaBalanceResults { - f.ClientView.Call("iotaBalance", nil) - return IotaBalanceResults{res: f.Results()} -} - -type IotaBalanceResults struct { - res wasmclient.Results -} - -func (r *IotaBalanceResults) Iotas() int64 { - return r.res.GetInt64(ResIotas) -} - -///////////////////////////// TestWasmLibService ///////////////////////////// - -type TestWasmLibService struct { - wasmclient.Service -} - -func NewTestWasmLibService(cl *wasmclient.ServiceClient, chainID string) (*TestWasmLibService, error) { - s := &TestWasmLibService{} - err := s.Service.Init(cl, chainID, 0x89703a45, EventHandlers) - return s, err -} - -func (s *TestWasmLibService) ArrayClear() ArrayClearFunc { - return ArrayClearFunc{ClientFunc: s.AsClientFunc()} -} - -func (s *TestWasmLibService) ArrayCreate() ArrayCreateFunc { - return ArrayCreateFunc{ClientFunc: s.AsClientFunc()} -} - -func (s *TestWasmLibService) ArraySet() ArraySetFunc { - return ArraySetFunc{ClientFunc: s.AsClientFunc()} -} - -func (s *TestWasmLibService) ParamTypes() ParamTypesFunc { - return ParamTypesFunc{ClientFunc: s.AsClientFunc()} -} - -func (s *TestWasmLibService) Random() RandomFunc { - return RandomFunc{ClientFunc: s.AsClientFunc()} -} - -func (s *TestWasmLibService) TriggerEvent() TriggerEventFunc { - return TriggerEventFunc{ClientFunc: s.AsClientFunc()} -} - -func (s *TestWasmLibService) ArrayLength() ArrayLengthView { - return ArrayLengthView{ClientView: s.AsClientView()} -} - -func (s *TestWasmLibService) ArrayValue() ArrayValueView { - return ArrayValueView{ClientView: s.AsClientView()} -} - -func (s *TestWasmLibService) BlockRecord() BlockRecordView { - return BlockRecordView{ClientView: s.AsClientView()} -} - -func (s *TestWasmLibService) BlockRecords() BlockRecordsView { - return BlockRecordsView{ClientView: s.AsClientView()} -} - -func (s *TestWasmLibService) GetRandom() GetRandomView { - return GetRandomView{ClientView: s.AsClientView()} -} - -func (s *TestWasmLibService) IotaBalance() IotaBalanceView { - return IotaBalanceView{ClientView: s.AsClientView()} -} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go deleted file mode 100644 index 205f7ffd4d..0000000000 --- a/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -package testwasmlibclient - -func OnTestWasmLibTest(event *EventTest) { -} diff --git a/contracts/wasm/testwasmlib/schema.yaml b/contracts/wasm/testwasmlib/schema.yaml index 3a9d9d8086..30f62287f1 100644 --- a/contracts/wasm/testwasmlib/schema.yaml +++ b/contracts/wasm/testwasmlib/schema.yaml @@ -68,6 +68,7 @@ funcs: uint64: Uint64? random: + triggerEvent: params: name: String diff --git a/contracts/wasm/testwasmlib/src/params.rs b/contracts/wasm/testwasmlib/src/params.rs index fa90c15fbd..5618d636fd 100644 --- a/contracts/wasm/testwasmlib/src/params.rs +++ b/contracts/wasm/testwasmlib/src/params.rs @@ -97,6 +97,7 @@ impl MutableArraySetParams { } } +#[derive(Clone, Copy)] pub struct MapStringToImmutableBytes { pub(crate) obj_id: i32, } @@ -190,6 +191,7 @@ impl ImmutableParamTypesParams { } } +#[derive(Clone, Copy)] pub struct MapStringToMutableBytes { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/testwasmlib/src/state.rs b/contracts/wasm/testwasmlib/src/state.rs index e2dfc69350..541fceaf7f 100644 --- a/contracts/wasm/testwasmlib/src/state.rs +++ b/contracts/wasm/testwasmlib/src/state.rs @@ -15,6 +15,7 @@ use crate::*; use crate::keys::*; use crate::typedefs::*; +#[derive(Clone, Copy)] pub struct MapStringToImmutableStringArray { pub(crate) obj_id: i32, } @@ -42,6 +43,7 @@ impl ImmutableTestWasmLibState { } } +#[derive(Clone, Copy)] pub struct MapStringToMutableStringArray { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/testwasmlib/src/typedefs.rs b/contracts/wasm/testwasmlib/src/typedefs.rs index 7c8e0d9e73..5a61439653 100644 --- a/contracts/wasm/testwasmlib/src/typedefs.rs +++ b/contracts/wasm/testwasmlib/src/typedefs.rs @@ -10,6 +10,7 @@ use wasmlib::*; use wasmlib::host::*; +#[derive(Clone, Copy)] pub struct ArrayOfImmutableString { pub(crate) obj_id: i32, } @@ -26,6 +27,7 @@ impl ArrayOfImmutableString { pub type ImmutableStringArray = ArrayOfImmutableString; +#[derive(Clone, Copy)] pub struct ArrayOfMutableString { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_test.go index 992d98a1a6..3cc625fccc 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_test.go @@ -8,9 +8,7 @@ import ( "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlib" - "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlibclient" "github.com/iotaledger/wasp/packages/solo" - "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" @@ -308,109 +306,3 @@ func TestMultiRandom(t *testing.T) { fmt.Printf("Random value: %d\n", number) } } - -// hardcoded seed and chain ID, taken from wasp-cli.json -// note that normally the chain has already been set up and -// the contract has already been deployed in some way, so -// these values are usually available from elsewhere -const ( - mySeed = "6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv" - myChainID = "jn52vSuUUYY22T1mV2ny14EADYBu3ofyewLRSsVRnjpz" -) - -func setupClient(t *testing.T) *testwasmlibclient.TestWasmLibService { - require.True(t, wasmclient.SeedIsValid(mySeed)) - require.True(t, wasmclient.ChainIsValid(myChainID)) - - // we're testing against wasp-cluster, so defaults will do - svcClient := wasmclient.DefaultServiceClient() - - // create the service for the testwasmlib smart contract - svc, err := testwasmlibclient.NewTestWasmLibService(svcClient, myChainID) - require.NoError(t, err) - - // we'll use the first address in the seed to sign requests - svc.SignRequests(wasmclient.SeedToKeyPair(mySeed, 0)) - return svc -} - -func TestClientEvents(t *testing.T) { - svc := setupClient(t) - - // get new triggerEvent interface, pass params, and post the request - f := svc.TriggerEvent() - f.Name("Lala") - f.Address(wasmclient.SeedToAddress(mySeed, 0)) - req1 := f.Post() - require.NoError(t, req1.Error()) - - // err := svc.WaitRequest(req1) - // require.NoError(t, err) - - // get new triggerEvent interface, pass params, and post the request - f = svc.TriggerEvent() - f.Name("Trala") - f.Address(wasmclient.SeedToAddress(mySeed, 1)) - req2 := f.Post() - require.NoError(t, req2.Error()) - - err := svc.WaitRequest(req2) - require.NoError(t, err) -} - -func TestClientRandom(t *testing.T) { - svc := setupClient(t) - - // generate new random value - f := svc.Random() - req := f.Post() - require.NoError(t, req.Error()) - - err := svc.WaitRequest(req) - require.NoError(t, err) - - // get current random value - v := svc.GetRandom() - res := v.Call() - require.NoError(t, v.Error()) - require.GreaterOrEqual(t, res.Random(), int64(0)) - fmt.Println("Random: ", res.Random()) -} - -func TestClientArray(t *testing.T) { - svc := setupClient(t) - - v := svc.ArrayLength() - v.Name("Bands") - res := v.Call() - require.NoError(t, v.Error()) - require.EqualValues(t, 0, res.Length()) - - f := svc.ArraySet() - f.Name("Bands") - f.Index(0) - f.Value("Dire Straits") - req := f.Post() - require.NoError(t, req.Error()) - err := svc.WaitRequest(req) - require.NoError(t, err) - - v = svc.ArrayLength() - v.Name("Bands") - res = v.Call() - require.NoError(t, v.Error()) - require.EqualValues(t, 1, res.Length()) - - c := svc.ArrayClear() - c.Name("Bands") - req = c.Post() - require.NoError(t, req.Error()) - err = svc.WaitRequest(req) - require.NoError(t, err) - - v = svc.ArrayLength() - v.Name("Bands") - res = v.Call() - require.NoError(t, v.Error()) - require.EqualValues(t, 0, res.Length()) -} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts deleted file mode 100644 index 2e11655cef..0000000000 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -import * as wasmclient from "wasmclient" -import * as app from "./testwasmlib" - -export const eventHandlers: wasmclient.EventHandlers = { - "testwasmlib.test": (msg: string[]) => app.onTestWasmLibTest(new EventTest(msg)), -}; - -export class EventTest extends wasmclient.Event { - public readonly address: wasmclient.Address; - public readonly name: wasmclient.String; - - public constructor(msg: string[]) { - super(msg) - this.address = this.nextAddress(); - this.name = this.nextString(); - } -} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts deleted file mode 100644 index f3eff69c33..0000000000 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -export * from "./testwasmlib"; -export * from "./events"; -export * from "./service"; diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts deleted file mode 100644 index 1446631506..0000000000 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -// (Re-)generated by schema tool -// >>>> DO NOT CHANGE THIS FILE! <<<< -// Change the json schema instead - -import * as wasmclient from "wasmclient" -import * as events from "./events" - -const ArgAddress = "address"; -const ArgAgentID = "agentID"; -const ArgBlockIndex = "blockIndex"; -const ArgBool = "bool"; -const ArgBytes = "bytes"; -const ArgChainID = "chainID"; -const ArgColor = "color"; -const ArgHash = "hash"; -const ArgHname = "hname"; -const ArgIndex = "index"; -const ArgInt16 = "int16"; -const ArgInt32 = "int32"; -const ArgInt64 = "int64"; -const ArgInt8 = "int8"; -const ArgName = "name"; -const ArgParam = "this"; -const ArgRecordIndex = "recordIndex"; -const ArgRequestID = "requestID"; -const ArgString = "string"; -const ArgUint16 = "uint16"; -const ArgUint32 = "uint32"; -const ArgUint64 = "uint64"; -const ArgUint8 = "uint8"; -const ArgValue = "value"; - -const ResCount = "count"; -const ResIotas = "iotas"; -const ResLength = "length"; -const ResRandom = "random"; -const ResRecord = "record"; -const ResValue = "value"; - -///////////////////////////// arrayClear ///////////////////////////// - -export class ArrayClearFunc extends wasmclient.ClientFunc { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public name(v: string): void { - this.args.setString(ArgName, v); - } - - public async post(): Promise { - this.args.mandatory(ArgName); - return await super.post(0x88021821, this.args); - } -} - -///////////////////////////// arrayCreate ///////////////////////////// - -export class ArrayCreateFunc extends wasmclient.ClientFunc { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public name(v: string): void { - this.args.setString(ArgName, v); - } - - public async post(): Promise { - this.args.mandatory(ArgName); - return await super.post(0x1ed5b23b, this.args); - } -} - -///////////////////////////// arraySet ///////////////////////////// - -export class ArraySetFunc extends wasmclient.ClientFunc { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public index(v: wasmclient.Int32): void { - this.args.setInt32(ArgIndex, v); - } - - public name(v: string): void { - this.args.setString(ArgName, v); - } - - public value(v: string): void { - this.args.setString(ArgValue, v); - } - - public async post(): Promise { - this.args.mandatory(ArgIndex); - this.args.mandatory(ArgName); - this.args.mandatory(ArgValue); - return await super.post(0x2c4150b3, this.args); - } -} - -///////////////////////////// paramTypes ///////////////////////////// - -export class ParamTypesFunc extends wasmclient.ClientFunc { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public address(v: wasmclient.Address): void { - this.args.setAddress(ArgAddress, v); - } - - public agentID(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgAgentID, v); - } - - public bool(v: boolean): void { - this.args.setBool(ArgBool, v); - } - - public bytes(v: wasmclient.Bytes): void { - this.args.setBytes(ArgBytes, v); - } - - public chainID(v: wasmclient.ChainID): void { - this.args.setChainID(ArgChainID, v); - } - - public color(v: wasmclient.Color): void { - this.args.setColor(ArgColor, v); - } - - public hash(v: wasmclient.Hash): void { - this.args.setHash(ArgHash, v); - } - - public hname(v: wasmclient.Hname): void { - this.args.setHname(ArgHname, v); - } - - public int16(v: wasmclient.Int16): void { - this.args.setInt16(ArgInt16, v); - } - - public int32(v: wasmclient.Int32): void { - this.args.setInt32(ArgInt32, v); - } - - public int64(v: wasmclient.Int64): void { - this.args.setInt64(ArgInt64, v); - } - - public int8(v: wasmclient.Int8): void { - this.args.setInt8(ArgInt8, v); - } - - public param(v: wasmclient.Bytes): void { - this.args.setBytes(ArgParam, v); - } - - public requestID(v: wasmclient.RequestID): void { - this.args.setRequestID(ArgRequestID, v); - } - - public string(v: string): void { - this.args.setString(ArgString, v); - } - - public uint16(v: wasmclient.Uint16): void { - this.args.setUint16(ArgUint16, v); - } - - public uint32(v: wasmclient.Uint32): void { - this.args.setUint32(ArgUint32, v); - } - - public uint64(v: wasmclient.Uint64): void { - this.args.setUint64(ArgUint64, v); - } - - public uint8(v: wasmclient.Uint8): void { - this.args.setUint8(ArgUint8, v); - } - - public async post(): Promise { - return await super.post(0x6921c4cd, this.args); - } -} - -///////////////////////////// random ///////////////////////////// - -export class RandomFunc extends wasmclient.ClientFunc { - - public async post(): Promise { - return await super.post(0xe86c97ca, null); - } -} - -///////////////////////////// triggerEvent ///////////////////////////// - -export class TriggerEventFunc extends wasmclient.ClientFunc { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public address(v: wasmclient.Address): void { - this.args.setAddress(ArgAddress, v); - } - - public name(v: string): void { - this.args.setString(ArgName, v); - } - - public async post(): Promise { - this.args.mandatory(ArgAddress); - this.args.mandatory(ArgName); - return await super.post(0xd5438ac6, this.args); - } -} - -///////////////////////////// arrayLength ///////////////////////////// - -export class ArrayLengthView extends wasmclient.ClientView { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public name(v: string): void { - this.args.setString(ArgName, v); - } - - public async call(): Promise { - this.args.mandatory(ArgName); - return new ArrayLengthResults(await this.callView("arrayLength", this.args)); - } -} - -export class ArrayLengthResults extends wasmclient.ViewResults { - - length(): wasmclient.Int32 { - return this.res.getInt32(ResLength); - } -} - -///////////////////////////// arrayValue ///////////////////////////// - -export class ArrayValueView extends wasmclient.ClientView { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public index(v: wasmclient.Int32): void { - this.args.setInt32(ArgIndex, v); - } - - public name(v: string): void { - this.args.setString(ArgName, v); - } - - public async call(): Promise { - this.args.mandatory(ArgIndex); - this.args.mandatory(ArgName); - return new ArrayValueResults(await this.callView("arrayValue", this.args)); - } -} - -export class ArrayValueResults extends wasmclient.ViewResults { - - value(): string { - return this.res.getString(ResValue); - } -} - -///////////////////////////// blockRecord ///////////////////////////// - -export class BlockRecordView extends wasmclient.ClientView { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public blockIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgBlockIndex, v); - } - - public recordIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgRecordIndex, v); - } - - public async call(): Promise { - this.args.mandatory(ArgBlockIndex); - this.args.mandatory(ArgRecordIndex); - return new BlockRecordResults(await this.callView("blockRecord", this.args)); - } -} - -export class BlockRecordResults extends wasmclient.ViewResults { - - record(): wasmclient.Bytes { - return this.res.getBytes(ResRecord); - } -} - -///////////////////////////// blockRecords ///////////////////////////// - -export class BlockRecordsView extends wasmclient.ClientView { - private args: wasmclient.Arguments = new wasmclient.Arguments(); - - public blockIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgBlockIndex, v); - } - - public async call(): Promise { - this.args.mandatory(ArgBlockIndex); - return new BlockRecordsResults(await this.callView("blockRecords", this.args)); - } -} - -export class BlockRecordsResults extends wasmclient.ViewResults { - - count(): wasmclient.Int32 { - return this.res.getInt32(ResCount); - } -} - -///////////////////////////// getRandom ///////////////////////////// - -export class GetRandomView extends wasmclient.ClientView { - - public async call(): Promise { - return new GetRandomResults(await this.callView("getRandom", null)); - } -} - -export class GetRandomResults extends wasmclient.ViewResults { - - random(): wasmclient.Int64 { - return this.res.getInt64(ResRandom); - } -} - -///////////////////////////// iotaBalance ///////////////////////////// - -export class IotaBalanceView extends wasmclient.ClientView { - - public async call(): Promise { - return new IotaBalanceResults(await this.callView("iotaBalance", null)); - } -} - -export class IotaBalanceResults extends wasmclient.ViewResults { - - iotas(): wasmclient.Int64 { - return this.res.getInt64(ResIotas); - } -} - -///////////////////////////// TestWasmLibService ///////////////////////////// - -export class TestWasmLibService extends wasmclient.Service { - - public constructor(cl: wasmclient.ServiceClient) { - super(cl, 0x89703a45, events.eventHandlers); - } - - public arrayClear(): ArrayClearFunc { - return new ArrayClearFunc(this); - } - - public arrayCreate(): ArrayCreateFunc { - return new ArrayCreateFunc(this); - } - - public arraySet(): ArraySetFunc { - return new ArraySetFunc(this); - } - - public paramTypes(): ParamTypesFunc { - return new ParamTypesFunc(this); - } - - public random(): RandomFunc { - return new RandomFunc(this); - } - - public triggerEvent(): TriggerEventFunc { - return new TriggerEventFunc(this); - } - - public arrayLength(): ArrayLengthView { - return new ArrayLengthView(this); - } - - public arrayValue(): ArrayValueView { - return new ArrayValueView(this); - } - - public blockRecord(): BlockRecordView { - return new BlockRecordView(this); - } - - public blockRecords(): BlockRecordsView { - return new BlockRecordsView(this); - } - - public getRandom(): GetRandomView { - return new GetRandomView(this); - } - - public iotaBalance(): IotaBalanceView { - return new IotaBalanceView(this); - } -} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts deleted file mode 100644 index cbae805e63..0000000000 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2020 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import * as events from "./events" -import * as service from "./service" - -export function onTestWasmLibTest(event: events.EventTest): void { -} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json b/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json deleted file mode 100644 index bc17186726..0000000000 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "lib": ["es2020"], - "target": "es2020", - "sourceMap": true - }, - "exclude": [ - "node_modules" - ], -} diff --git a/contracts/wasm/tokenregistry/src/state.rs b/contracts/wasm/tokenregistry/src/state.rs index 3726c7b438..663b3d978a 100644 --- a/contracts/wasm/tokenregistry/src/state.rs +++ b/contracts/wasm/tokenregistry/src/state.rs @@ -15,6 +15,7 @@ use crate::*; use crate::keys::*; use crate::structs::*; +#[derive(Clone, Copy)] pub struct ArrayOfImmutableColor { pub(crate) obj_id: i32, } @@ -29,6 +30,7 @@ impl ArrayOfImmutableColor { } } +#[derive(Clone, Copy)] pub struct MapColorToImmutableToken { pub(crate) obj_id: i32, } @@ -56,6 +58,7 @@ impl ImmutableTokenRegistryState { } } +#[derive(Clone, Copy)] pub struct ArrayOfMutableColor { pub(crate) obj_id: i32, } @@ -74,6 +77,7 @@ impl ArrayOfMutableColor { } } +#[derive(Clone, Copy)] pub struct MapColorToMutableToken { pub(crate) obj_id: i32, } diff --git a/contracts/wasm/tokenregistry/src/structs.rs b/contracts/wasm/tokenregistry/src/structs.rs index d5dd0a4c6b..3fce40d7f1 100644 --- a/contracts/wasm/tokenregistry/src/structs.rs +++ b/contracts/wasm/tokenregistry/src/structs.rs @@ -11,6 +11,7 @@ use wasmlib::*; use wasmlib::host::*; +#[derive(Clone)] pub struct Token { pub created : i64, // creation timestamp pub description : String, // description what minted token represents @@ -48,6 +49,7 @@ impl Token { } } +#[derive(Clone, Copy)] pub struct ImmutableToken { pub(crate) obj_id: i32, pub(crate) key_id: Key32, @@ -63,6 +65,7 @@ impl ImmutableToken { } } +#[derive(Clone, Copy)] pub struct MutableToken { pub(crate) obj_id: i32, pub(crate) key_id: Key32, diff --git a/packages/vm/wasmlib/src/coreaccounts/results.rs b/packages/vm/wasmlib/src/coreaccounts/results.rs index 918522561e..463868574a 100644 --- a/packages/vm/wasmlib/src/coreaccounts/results.rs +++ b/packages/vm/wasmlib/src/coreaccounts/results.rs @@ -12,6 +12,7 @@ use crate::*; use crate::coreaccounts::*; use crate::host::*; +#[derive(Clone, Copy)] pub struct MapAgentIDToImmutableBytes { pub(crate) obj_id: i32, } @@ -33,6 +34,7 @@ impl ImmutableAccountsResults { } } +#[derive(Clone, Copy)] pub struct MapAgentIDToMutableBytes { pub(crate) obj_id: i32, } @@ -58,6 +60,7 @@ impl MutableAccountsResults { } } +#[derive(Clone, Copy)] pub struct MapColorToImmutableInt64 { pub(crate) obj_id: i32, } @@ -79,6 +82,7 @@ impl ImmutableBalanceResults { } } +#[derive(Clone, Copy)] pub struct MapColorToMutableInt64 { pub(crate) obj_id: i32, } diff --git a/packages/vm/wasmlib/src/coreblob/params.rs b/packages/vm/wasmlib/src/coreblob/params.rs index 54c0d9bebe..736edcdcfd 100644 --- a/packages/vm/wasmlib/src/coreblob/params.rs +++ b/packages/vm/wasmlib/src/coreblob/params.rs @@ -12,6 +12,7 @@ use crate::*; use crate::coreblob::*; use crate::host::*; +#[derive(Clone, Copy)] pub struct MapStringToImmutableBytes { pub(crate) obj_id: i32, } @@ -33,6 +34,7 @@ impl ImmutableStoreBlobParams { } } +#[derive(Clone, Copy)] pub struct MapStringToMutableBytes { pub(crate) obj_id: i32, } diff --git a/packages/vm/wasmlib/src/coreblob/results.rs b/packages/vm/wasmlib/src/coreblob/results.rs index dd06064b2e..c040ca1241 100644 --- a/packages/vm/wasmlib/src/coreblob/results.rs +++ b/packages/vm/wasmlib/src/coreblob/results.rs @@ -56,6 +56,7 @@ impl MutableGetBlobFieldResults { } } +#[derive(Clone, Copy)] pub struct MapStringToImmutableInt32 { pub(crate) obj_id: i32, } @@ -77,6 +78,7 @@ impl ImmutableGetBlobInfoResults { } } +#[derive(Clone, Copy)] pub struct MapStringToMutableInt32 { pub(crate) obj_id: i32, } @@ -102,6 +104,7 @@ impl MutableGetBlobInfoResults { } } +#[derive(Clone, Copy)] pub struct MapHashToImmutableInt32 { pub(crate) obj_id: i32, } @@ -123,6 +126,7 @@ impl ImmutableListBlobsResults { } } +#[derive(Clone, Copy)] pub struct MapHashToMutableInt32 { pub(crate) obj_id: i32, } diff --git a/packages/vm/wasmlib/src/coreblocklog/results.rs b/packages/vm/wasmlib/src/coreblocklog/results.rs index 19809b8973..98d0efaf24 100644 --- a/packages/vm/wasmlib/src/coreblocklog/results.rs +++ b/packages/vm/wasmlib/src/coreblocklog/results.rs @@ -72,6 +72,7 @@ impl MutableGetBlockInfoResults { } } +#[derive(Clone, Copy)] pub struct ArrayOfImmutableBytes { pub(crate) obj_id: i32, } @@ -98,6 +99,7 @@ impl ImmutableGetEventsForBlockResults { } } +#[derive(Clone, Copy)] pub struct ArrayOfMutableBytes { pub(crate) obj_id: i32, } @@ -206,6 +208,7 @@ impl MutableGetLatestBlockInfoResults { } } +#[derive(Clone, Copy)] pub struct ArrayOfImmutableRequestID { pub(crate) obj_id: i32, } @@ -232,6 +235,7 @@ impl ImmutableGetRequestIDsForBlockResults { } } +#[derive(Clone, Copy)] pub struct ArrayOfMutableRequestID { pub(crate) obj_id: i32, } diff --git a/packages/vm/wasmlib/src/coregovernance/results.rs b/packages/vm/wasmlib/src/coregovernance/results.rs index 0c5e2c9709..d834e234b7 100644 --- a/packages/vm/wasmlib/src/coregovernance/results.rs +++ b/packages/vm/wasmlib/src/coregovernance/results.rs @@ -12,6 +12,7 @@ use crate::*; use crate::coregovernance::*; use crate::host::*; +#[derive(Clone, Copy)] pub struct ArrayOfImmutableBytes { pub(crate) obj_id: i32, } @@ -38,6 +39,7 @@ impl ImmutableGetAllowedStateControllerAddressesResults { } } +#[derive(Clone, Copy)] pub struct ArrayOfMutableBytes { pub(crate) obj_id: i32, } diff --git a/packages/vm/wasmlib/src/coreroot/results.rs b/packages/vm/wasmlib/src/coreroot/results.rs index 9a4f88fdb5..933c193392 100644 --- a/packages/vm/wasmlib/src/coreroot/results.rs +++ b/packages/vm/wasmlib/src/coreroot/results.rs @@ -42,6 +42,7 @@ impl MutableFindContractResults { } } +#[derive(Clone, Copy)] pub struct MapHnameToImmutableBytes { pub(crate) obj_id: i32, } @@ -64,6 +65,7 @@ impl ImmutableGetContractRecordsResults { } } +#[derive(Clone, Copy)] pub struct MapHnameToMutableBytes { pub(crate) obj_id: i32, } diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 3a30f87730..dbd8f7901a 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -32,7 +32,8 @@ export class Service { public async postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): Promise { // get request essence ready for signing - let essence = Base58.decode(this.serviceClient.configuration.chainId); + const chainID = this.serviceClient.configuration.chainId; + let essence = Base58.decode(chainID); const hNames = Buffer.alloc(8) hNames.writeUInt32LE(this.scHname, 0); hNames.writeUInt32LE(hFuncName, 4); @@ -47,7 +48,26 @@ export class Service { const hash = Hash.from(buf); const requestID = Buffer.concat([hash, Buffer.alloc(2)]); - await this.serviceClient.waspClient.postRequest(this.serviceClient.configuration.chainId, buf); + await this.serviceClient.waspClient.postRequest(chainID, buf); + return Base58.encode(requestID); + } + + public async postOnLedgerRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): Promise { + // get request essence ready for signing + const config = this.serviceClient.configuration; + let essence = Buffer.alloc(13) + essence.writeUInt32LE(this.scHname, 4); + essence.writeUInt32LE(hFuncName, 8); + essence = Buffer.concat([essence, args.encode()]); + + let buf = Buffer.alloc(1); + const requestTypeOffledger = 1; + buf.writeUInt8(requestTypeOffledger, 0); + buf = Buffer.concat([buf, essence, ED25519.privateSign(keyPair, essence)]); + const hash = Hash.from(buf); + const requestID = Buffer.concat([hash, Buffer.alloc(2)]); + + await this.serviceClient.waspClient.postOnLedgerRequest(config.chainId, buf); return Base58.encode(requestID); } diff --git a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts index f05fc2ed36..658c412262 100644 --- a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts @@ -8,9 +8,9 @@ export class ServiceClient { waspClient: wasmclient.WaspClient; configuration: Configuration; - constructor(configuration: Configuration) { - this.configuration = configuration; - this.waspClient = new wasmclient.WaspClient(configuration.waspApiUrl); + constructor(config: Configuration) { + this.configuration = config; + this.waspClient = new wasmclient.WaspClient(config.waspApiUrl, config.goShimmerApiUrl); } static default(): ServiceClient { @@ -19,7 +19,7 @@ export class ServiceClient { seed: null, waspWebSocketUrl: "ws://127.0.0.1:9090", waspApiUrl: "127.0.0.1:9090", - goShimmerApiUrl: "" + goShimmerApiUrl: "127.0.0.1:8080" }; return new ServiceClient(new Configuration(defaultConfiguration)); // "127.0.0.1:5550"); } diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 733d881d6f..4fe159e26b 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -21,25 +21,38 @@ interface ICallViewResponse extends IResponse { Items: [{ Key: string; Value: string }]; } +export interface ISendTransactionRequest { + txn_bytes: string; +} + +export interface ISendTransactionResponse extends IResponse { + transaction_id?: string; +} + interface IOffLedgerRequest { Request: string; } export class WaspClient { private waspAPI: string; + private goshimmerAPI: string; - constructor(waspAPI: string) { - if(waspAPI.startsWith("https://") || waspAPI.startsWith("http://")) - this.waspAPI = waspAPI; - else - this.waspAPI = "http://" + waspAPI; + constructor(waspAPI: string, goshimmerAPI: string) { + this.waspAPI = waspAPI; + if (!waspAPI.startsWith("http")) { + this.waspAPI = "http://" + waspAPI; + } + this.goshimmerAPI = goshimmerAPI; + if (!goshimmerAPI.startsWith("http")) { + this.goshimmerAPI = "http://" + goshimmerAPI; + } } public async callView(chainID: string, contractHName: string, entryPoint: string, args: Buffer): Promise { const request = {Request: args.toString("base64")}; const result = await this.sendRequest( "post", - `/chain/${chainID}/contract/${contractHName}/callview/${entryPoint}`, + this.waspAPI + `/chain/${chainID}/contract/${contractHName}/callview/${entryPoint}`, request ); const res = new wasmclient.Results(); @@ -58,15 +71,25 @@ export class WaspClient { const request = {Request: offLedgerRequest.toString("base64")}; await this.sendRequest( "post", - `/request/${chainID}`, + this.waspAPI + `/request/${chainID}`, + request, + ); + } + + public async postOnLedgerRequest(chainID: string, onLedgerRequest: Buffer): Promise { + const request = {txn_bytes: onLedgerRequest.toString("base64")}; + const response = await this.sendRequest( + "post", + this.goshimmerAPI + `/ledgerstate/transactions`, request, ); + return response.body; } public async waitRequest(chainID: string, reqID: wasmclient.RequestID): Promise { await this.sendRequest( "get", - `/chain/${chainID}/request/${reqID}/wait`, + this.waspAPI + `/chain/${chainID}/request/${reqID}/wait`, ); } @@ -79,8 +102,7 @@ export class WaspClient { let fetchResponse: Response; try { - const url = this.waspAPI + path; - fetchResponse = await fetch(url, { + fetchResponse = await fetch(path, { method: verb, headers, body: JSON.stringify(request), diff --git a/tools/schema/generator/rstemplates/structs.go b/tools/schema/generator/rstemplates/structs.go index 4c74de9f0e..6d0e1fc499 100644 --- a/tools/schema/generator/rstemplates/structs.go +++ b/tools/schema/generator/rstemplates/structs.go @@ -14,6 +14,7 @@ $#each structs structType // ******************************* "structType": ` +#[derive(Clone)] pub struct $StrName { $#each struct structField } @@ -52,6 +53,7 @@ $#emit structMethods // ******************************* "structMethods": ` +#[derive(Clone, Copy)] pub struct $mut$StrName { pub(crate) obj_id: i32, pub(crate) key_id: Key32, diff --git a/tools/schema/generator/rstemplates/typedefs.go b/tools/schema/generator/rstemplates/typedefs.go index 3b6a2fd676..06a3e60021 100644 --- a/tools/schema/generator/rstemplates/typedefs.go +++ b/tools/schema/generator/rstemplates/typedefs.go @@ -36,6 +36,7 @@ $#if exist else typedefProxyArrayNew // ******************************* "typedefProxyArrayNew": ` +#[derive(Clone, Copy)] pub struct $proxy { pub(crate) obj_id: i32, } @@ -89,6 +90,7 @@ $#if exist else typedefProxyMapNew // ******************************* "typedefProxyMapNew": ` +#[derive(Clone, Copy)] pub struct $proxy { pub(crate) obj_id: i32, } From 445bb628eadefb489d7feb4bdcd67f764c28fd38 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Wed, 5 Jan 2022 05:04:45 +0100 Subject: [PATCH 050/120] Added goshimmer client with faucet request and pow background worker. --- .../wasmclient/api_common/request_sender.ts | 53 ++++++++ .../wasmclient/api_common/response_models.ts | 8 ++ .../goshimmer/faucet/faucet_helper.ts | 30 +++++ .../goshimmer/faucet/faucet_models.ts | 18 +++ .../wasmclient/goshimmer/goshimmerclient.ts | 118 ++++++++++++++++++ .../ts/wasmclient/goshimmer/models/mana.ts | 13 ++ .../goshimmer/models/unspent_outputs.ts | 69 ++++++++++ .../goshimmer/pow_web_worker/pow.worker.ts | 21 ++++ .../pow_web_worker/pow_worker_manager.ts | 54 ++++++++ .../goshimmer/pow_web_worker/proof_of_work.ts | 33 +++++ .../pow_web_worker/worker-loader.d.ts | 10 ++ 11 files changed, 427 insertions(+) create mode 100644 packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/api_common/response_models.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/faucet/faucet_helper.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/faucet/faucet_models.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/models/mana.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/pow.worker.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/pow_worker_manager.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/proof_of_work.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/worker-loader.d.ts diff --git a/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts b/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts new file mode 100644 index 0000000000..378e5dd58b --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts @@ -0,0 +1,53 @@ +import { IResponse, IExtendedResponse } from './response_models'; + +const headers: { [id: string]: string } = { + 'Content-Type': 'application/json', +}; + +export async function sendRequest( + url: string, + verb: 'put' | 'post' | 'get' | 'delete', + path: string, + request?: T | undefined +): Promise { + const response = await sendRequestExt(url, verb, path, request); + return response.body; +} + +export async function sendRequestExt( + apiUrl: string, + verb: 'put' | 'post' | 'get' | 'delete', + path: string, + request?: T | undefined +): Promise> { + let fetchResponse: Response; + + try { + const url = `${apiUrl}/${path}`; + fetchResponse = await fetch(url, { + method: verb, + headers, + body: JSON.stringify(request), + }); + + if (!fetchResponse) { + throw new Error('No data was returned from the API'); + } + + try { + const response = await fetchResponse.json(); + return { body: response, response: fetchResponse }; + } catch (err) { + const error = err as Error; + if (fetchResponse.ok) { + throw new Error(error.message); + } else { + const text = await fetchResponse.text(); + throw new Error(error.message + ' --- ' + text); + } + } + } catch (err) { + const error = err as Error; + throw new Error('sendRequest: ' + error.message); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/api_common/response_models.ts b/packages/vm/wasmlib/ts/wasmclient/api_common/response_models.ts new file mode 100644 index 0000000000..ca1faaa082 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/api_common/response_models.ts @@ -0,0 +1,8 @@ +export interface IResponse { + error?: string; +} + +export interface IExtendedResponse { + body: U; + response: Response; +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/faucet/faucet_helper.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/faucet/faucet_helper.ts new file mode 100644 index 0000000000..bda8a9582c --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/faucet/faucet_helper.ts @@ -0,0 +1,30 @@ +import { Buffer } from '../../buffer'; +import { Base58 } from '../../crypto'; +import { IFaucetRequest } from './faucet_models'; + +export class FaucetHelper { + public static ToBuffer(faucetRequest: IFaucetRequest): Buffer { + const buffers = []; + + const payloadLen = Buffer.alloc(4); + payloadLen.writeUInt32LE(109, 0); + buffers.push(payloadLen); + + const faucetRequestType = Buffer.alloc(4); + faucetRequestType.writeUInt32LE(2, 0); + buffers.push(faucetRequestType); + + const addressBytes = Base58.decode(faucetRequest.address); + buffers.push(addressBytes); + + const aManaPledgeBytes = Base58.decode(faucetRequest.accessManaPledgeID); + buffers.push(aManaPledgeBytes); + + const cManaPledgeBytes = Base58.decode(faucetRequest.consensusManaPledgeID); + buffers.push(cManaPledgeBytes); + + const data = Buffer.concat(buffers); + + return data; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/faucet/faucet_models.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/faucet/faucet_models.ts new file mode 100644 index 0000000000..9da4521dbc --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/faucet/faucet_models.ts @@ -0,0 +1,18 @@ +import type { IResponse } from '../../api_common/response_models'; +import { Buffer } from '../../buffer'; + +export interface IFaucetRequest { + accessManaPledgeID: string; + consensusManaPledgeID: string; + address: string; + nonce: number; +} + +export interface IFaucetResponse extends IResponse { + id?: string; +} + +export interface IFaucetRequestContext { + faucetRequest: IFaucetRequest; + poWBuffer: Buffer; +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts new file mode 100644 index 0000000000..1cfe80c550 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts @@ -0,0 +1,118 @@ +import { Buffer } from '../buffer'; +import { Configuration } from '../configuration'; + +import { IFaucetRequest, IFaucetRequestContext, IFaucetResponse } from './faucet/faucet_models'; +import { FaucetHelper } from './faucet/faucet_helper'; + +import { IUnspentOutputsRequest, IUnspentOutputsResponse } from './models/unspent_outputs'; +import { IAllowedManaPledgeResponse } from './models/mana'; + +import { PoWWorkerManager } from './pow_web_worker/pow_worker_manager'; + +import * as requestSender from '../api_common/request_sender'; + +interface GoShimmerClientConfiguration { + APIUrl: string; + SeedUnsafe: Buffer | null; +} + +const IOTA_COLOR: string = 'IOTA'; + +export class GoShimmerClient { + private readonly goShimmerConfiguration: GoShimmerClientConfiguration; + private readonly powManager: PoWWorkerManager = new PoWWorkerManager(); + + constructor(configuration: Configuration) { + this.goShimmerConfiguration = { APIUrl: configuration.goShimmerApiUrl, SeedUnsafe: configuration.seed }; + } + + public async getIOTABalance(address: string): Promise { + const iotaBalance = await this.getBalance(address, IOTA_COLOR); + return iotaBalance; + } + + private async getBalance(address: string, color: string): Promise { + if (color == IOTA_COLOR) { + color = '11111111111111111111111111111111'; + } + + const unspents = await this.unspentOutputs({ addresses: [address] }); + const currentUnspent = unspents.unspentOutputs.find((x) => x.address.base58 == address); + + const balance = currentUnspent!.outputs + .filter( + (o) => + ['ExtendedLockedOutputType', 'SigLockedColoredOutputType'].includes(o.output.type) && + typeof o.output.output.balances[color] != 'undefined' + ) + .map((uid) => uid.output.output.balances) + .reduce((balance: bigint, output) => (balance += BigInt(output[color])), BigInt(0)); + + return balance; + } + + private async unspentOutputs(request: IUnspentOutputsRequest): Promise { + return requestSender.sendRequest( + this.goShimmerConfiguration.APIUrl, + 'post', + 'ledgerstate/addresses/unspentOutputs', + request + ); + } + + public async requestFunds(address: string): Promise { + try { + const faucetRequestContext = await this.getFaucetRequest(address); + const response = await this.sendFaucetRequest(faucetRequestContext.faucetRequest); + const success = response.error === undefined && response.id !== undefined; + return success; + } catch (ex: unknown) { + const error = ex as Error; + console.error(error.message); + return false; + } + } + + private async getFaucetRequest(address: string): Promise { + const manaPledge = await this.getAllowedManaPledge(); + + const allowedManagePledge = manaPledge.accessMana?.allowed ? manaPledge.accessMana.allowed[0] : ''; + const consenseusManaPledge = manaPledge.consensusMana?.allowed ? manaPledge.consensusMana?.allowed[0] : ''; + + const body: IFaucetRequest = { + accessManaPledgeID: allowedManagePledge, + consensusManaPledgeID: consenseusManaPledge, + address: address, + nonce: -1, + }; + + const poWBuffer = FaucetHelper.ToBuffer(body); + + body.nonce = await this.powManager.requestProofOfWork(12, poWBuffer); + + const result: IFaucetRequestContext = { + poWBuffer: poWBuffer, + faucetRequest: body, + }; + + return result; + } + + private async getAllowedManaPledge(): Promise { + return requestSender.sendRequest( + this.goShimmerConfiguration.APIUrl, + 'get', + 'mana/allowedManaPledge' + ); + } + + private async sendFaucetRequest(faucetRequest: IFaucetRequest): Promise { + const response = await requestSender.sendRequest( + this.goShimmerConfiguration.APIUrl, + 'post', + 'faucet', + faucetRequest + ); + return response; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/mana.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/mana.ts new file mode 100644 index 0000000000..786cd44507 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/mana.ts @@ -0,0 +1,13 @@ +import type { IResponse } from '../../api_common/response_models'; + +export interface IAllowedManaPledgeResponse extends IResponse { + accessMana: { + isFilterEnabled: boolean; + allowed?: Array; + }; + + consensusMana: { + isFilterEnabled: boolean; + allowed?: Array; + }; +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts new file mode 100644 index 0000000000..d834254738 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts @@ -0,0 +1,69 @@ +import type { IResponse } from '../../api_common/response_models'; + +export interface IUnspentOutputsRequest { + addresses: string[]; +} + +export interface IUnspentOutput { + outputID: { + base58: string; + transactionID: string; + outputIndex: number; + }; + + type: string; + + output: { + balances: { + [color: string]: bigint; + }; + + address: string; + }; + + inclusionState: { + confirmed?: boolean; + rejected?: boolean; + conflicting?: boolean; + }; +} + +export interface IUnspentOutputAddress { + type: string; + base58: string; +} + +export type ColorCollection = { [key: string]: bigint }; + +export interface IUnspentOutputMap { + address: string; + outputs: { + id: string; + balances: ColorCollection; + inclusionState: { + confirmed?: boolean; + rejected?: boolean; + conflicting?: boolean; + }; + }[]; +} + +export interface IUnspentOutputsResponse extends IResponse { + unspentOutputs: { + address: IUnspentOutputAddress; + outputs: { + output: IUnspentOutput; + + inclusionState: { + confirmed?: boolean; + rejected?: boolean; + conflicting?: boolean; + }; + }[]; + }[]; +} + +export interface ISingleUnspentOutputResponse extends IResponse { + address: IUnspentOutputAddress; + outputs: IUnspentOutput[]; +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/pow.worker.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/pow.worker.ts new file mode 100644 index 0000000000..6d2f4bfd01 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/pow.worker.ts @@ -0,0 +1,21 @@ +import ProofOfWork from './proof_of_work'; +import type { PowWorkerRequest, PowWorkerResponse } from './pow_worker_manager'; + +const ctx: Worker = self as never; + +ctx.onmessage = (e) => { + const message: PowWorkerRequest = e.data; + if (message.type != 'pow_request') return; + + try { + console.log(`[${message.uuid}] Starting PoW!`); + const nonce = ProofOfWork.calculateProofOfWork(message.difficulty, message.data); + console.log(`[${message.uuid}] PoW Done!`); + + const response: PowWorkerResponse = { type: 'pow_response', data: nonce, uuid: message.uuid }; + ctx.postMessage(response); + } catch (ex) { + ctx.postMessage({ type: 'pow_response', error: ex, uuid: message.uuid }); + console.error('PoW failed!'); + } +}; diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/pow_worker_manager.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/pow_worker_manager.ts new file mode 100644 index 0000000000..18dded42f8 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/pow_worker_manager.ts @@ -0,0 +1,54 @@ +import { v4 as uuidv4 } from 'uuid'; +import type { Buffer } from '../../buffer'; +import Worker from 'worker-loader!./pow.worker.ts'; + +export interface PowWorkerRequest { + type: string; + data: Buffer; + uuid: string; + difficulty: number; +} + +export interface PowWorkerResponse { + type: string; + data: number; + uuid: string; + error?: Error; +} + +export class PoWWorkerManager { + private powWorker: Worker = new Worker(); + + public requestProofOfWork(difficulty: number, data: Buffer): Promise { + return new Promise((resolve, reject) => { + if (this.powWorker == null) throw new Error('powWorker not defined'); + + const requestId = uuidv4(); + + const responseHandler = (e: MessageEvent) => { + const message: PowWorkerResponse = e.data; + + if (message.type == 'pow_response' && message.uuid == requestId) { + this.powWorker!.removeEventListener('message', responseHandler); + + if (!message.error) { + resolve(message.data); + } else { + reject(message.error); + } + } + }; + + this.powWorker.addEventListener('message', responseHandler); + + const request: PowWorkerRequest = { + type: 'pow_request', + data: data, + difficulty: difficulty, + uuid: requestId, + }; + + this.powWorker.postMessage(request); + }); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/proof_of_work.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/proof_of_work.ts new file mode 100644 index 0000000000..81b4cc5aa7 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/proof_of_work.ts @@ -0,0 +1,33 @@ +import { blake2b } from 'blakejs'; +import { Buffer } from '../../buffer'; + +export default class ProofOfWork { + public static calculateProofOfWork(target: number, message: Buffer): number { + for (let nonce = 0; ; nonce++) { + const nonceLE = this.numberToUInt64LE(BigInt(nonce)); + const data = Buffer.concat([message, nonceLE]); + + const digest = blake2b(data); + const b = Buffer.alloc(4); + + for (let i = 0; i < 4; i++) { + b[i] = digest[i]; + } + + const leadingZeros = Math.clz32(b.readUInt32BE(0)); + + if (leadingZeros >= target) { + // eslint-disable-next-line no-console + console.log('PoW Single Thread done'); + return nonce; + } + } + } + + private static numberToUInt64LE(n: bigint): Buffer { + const buffer = Buffer.alloc(8); + buffer.writeBigUInt64LE(n, 0); + + return buffer; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/worker-loader.d.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/worker-loader.d.ts new file mode 100644 index 0000000000..f019a00ac8 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/pow_web_worker/worker-loader.d.ts @@ -0,0 +1,10 @@ +declare module 'worker-loader!*' { + // You need to change `Worker`, if you specified a different value for the `workerType` option + class WebpackWorker extends Worker { + constructor(); + } + + // Uncomment this if you set the `esModule` option to `false` + // export = WebpackWorker; + export default WebpackWorker; +} From 2ce7f1d0f9a95ff56a8ad6408f1aba47e842eb80 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 5 Jan 2022 11:03:10 +0200 Subject: [PATCH 051/120] More tests are passing. --- packages/chain/chain.go | 1 + packages/chain/chainimpl/chainimpl.go | 14 +- packages/chain/chainimpl/eventproc.go | 2 +- .../chain/statemgr/domain_with_fallback.go | 135 ++++++++++++++++++ packages/chain/statemgr/eventproc.go | 2 +- packages/chain/statemgr/setup_test.go | 4 +- packages/chain/statemgr/statemgr.go | 23 ++- packages/chain/statemgr/statemgr_test.go | 56 +++++--- packages/chain/statemgr/syncmgr.go | 18 ++- packages/peering/domain/domain.go | 23 ++- packages/peering/domain/domain_test.go | 4 +- packages/peering/peering.go | 3 +- packages/testutil/trustedNetworkManager.go | 3 + .../cluster/tests/advanced_inccounter_test.go | 20 ++- 14 files changed, 252 insertions(+), 56 deletions(-) create mode 100644 packages/chain/statemgr/domain_with_fallback.go diff --git a/packages/chain/chain.go b/packages/chain/chain.go index de7e93332c..c4d72a7d8c 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -148,6 +148,7 @@ type StateManager interface { EnqueueStateCandidateMsg(state.VirtualStateAccess, ledgerstate.OutputID) EnqueueTimerMsg(msg messages.TimerTick) GetStatusSnapshot() *SyncInfo + SetChainPeers(peers []*ed25519.PublicKey) Close() } diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index 87590956b4..67ec227cce 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -142,12 +142,19 @@ func NewChain( ret.committee.Store(&committeeStruct{}) var err error - ret.chainPeers, err = netProvider.PeerDomain(chainID.Array(), []*ed25519.PublicKey{netProvider.Self().PubKey()}) + chainPeerNodes := []*ed25519.PublicKey{netProvider.Self().PubKey()} + ret.chainPeers, err = netProvider.PeerDomain(chainID.Array(), chainPeerNodes) if err != nil { - log.Errorf("NewChain: %v", err) + log.Errorf("NewChain: unable to create chainPeers domain: %v", err) return nil } - ret.stateMgr = statemgr.New(db, ret, ret.chainPeers, ret.nodeConn, chainMetrics) + stateMgrDomain, err := statemgr.NewDomainWithFallback(chainID.Array(), netProvider, log.Named("sm")) + if err != nil { + log.Errorf("NewChain: unable to create stateMgr.fallbackPeers domain: %v", err) + return nil + } + ret.stateMgr = statemgr.New(db, ret, stateMgrDomain, ret.nodeConn, chainMetrics) + ret.stateMgr.SetChainPeers(chainPeerNodes) ret.eventChainTransitionClosure = events.NewClosure(ret.processChainTransition) ret.eventChainTransition.Attach(ret.eventChainTransitionClosure) @@ -322,6 +329,7 @@ func (c *chainObj) updateChainNodes() { newMemberList = append(newMemberList, &pubKeyCopy) } c.chainPeers.UpdatePeers(newMemberList) + c.stateMgr.SetChainPeers(newMemberList) // // Remember the candidate nodes as well (as a cache). diff --git a/packages/chain/chainimpl/eventproc.go b/packages/chain/chainimpl/eventproc.go index 6282fa14b8..1eb70a4939 100644 --- a/packages/chain/chainimpl/eventproc.go +++ b/packages/chain/chainimpl/eventproc.go @@ -275,7 +275,7 @@ func (c *chainObj) broadcastOffLedgerRequest(req *request.OffLedger) { Req: req, } cmt := c.getCommittee() - getPeerPubKeys := c.chainPeers.GetRandomPeers + getPeerPubKeys := c.chainPeers.GetRandomOtherPeers if cmt != nil { getPeerPubKeys = cmt.GetRandomValidators diff --git a/packages/chain/statemgr/domain_with_fallback.go b/packages/chain/statemgr/domain_with_fallback.go new file mode 100644 index 0000000000..e82a342503 --- /dev/null +++ b/packages/chain/statemgr/domain_with_fallback.go @@ -0,0 +1,135 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package statemgr + +import ( + "context" + "time" + + "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/logger" + "github.com/iotaledger/wasp/packages/peering" + "golang.org/x/xerrors" +) + +// DomainWithFallback acts as a peering domain, but maintains 2 sets of peers, +// one for the normal operation (matches with the chain peers) and other for +// the fallback mode (include all the trusted peers). +type DomainWithFallback struct { + ctx context.Context + ctxCancel context.CancelFunc + net peering.NetworkProvider + dom peering.PeerDomainProvider + fallback bool + mainPeers []*ed25519.PublicKey + log *logger.Logger +} + +func NewDomainWithFallback(peeringID peering.PeeringID, net peering.NetworkProvider, log *logger.Logger) (*DomainWithFallback, error) { + dom, err := net.PeerDomain(peeringID, make([]*ed25519.PublicKey, 0)) + if err != nil { + return nil, xerrors.Errorf("unable to allocate peer domain: %w", err) + } + ctx, ctxCancel := context.WithCancel(context.Background()) + df := DomainWithFallback{ + ctx: ctx, + ctxCancel: ctxCancel, + net: net, + dom: dom, + fallback: false, + mainPeers: make([]*ed25519.PublicKey, 0), + log: log, + } + go df.run() + return &df, nil +} + +func (df *DomainWithFallback) run() { + for { + select { + case <-df.ctx.Done(): + return + case <-time.After(1 * time.Second): + if df.fallback { + // Some peers could be made trusted/untrusted. Some can go online/offline. + // Thus we need to update the list of peers periodically. + // That is only needed of the fallback list is in use. + df.updateDomainPeers() + } + } + } +} + +// SetMainPeers updates the peer list as it is reported by the chain. +// We exclude the self peer here, because we use this to send messages to other nodes. +func (df *DomainWithFallback) SetMainPeers(peers []*ed25519.PublicKey) { + selfPubKey := df.net.Self().PubKey() + otherPeers := make([]*ed25519.PublicKey, 0) + for i := range peers { + if *peers[i] != *selfPubKey { + otherPeers = append(otherPeers, peers[i]) + } + } + df.log.Debugf("SetMainPeers: number of other mainPeers: %v, self dropped=%v", len(otherPeers), len(peers) != len(otherPeers)) + df.mainPeers = otherPeers + if !df.fallback { + df.updateDomainPeers() + } +} + +func (df *DomainWithFallback) HaveMainPeers() bool { + return len(df.mainPeers) > 0 +} + +func (df *DomainWithFallback) SetFallbackMode(fallback bool) { + if df.fallback == fallback { + return + } + df.log.Debugf("SetFallbackMode: fallback=%v", fallback) + df.fallback = fallback + df.updateDomainPeers() +} + +func (df *DomainWithFallback) GetFallbackMode() bool { + return df.fallback +} + +func (df *DomainWithFallback) updateDomainPeers() { + var peers []*ed25519.PublicKey + if df.fallback { + selfPubKey := df.net.Self().PubKey() + allTrusted := make([]*ed25519.PublicKey, 0) + for _, n := range df.net.PeerStatus() { + if n.IsAlive() && *n.PubKey() != *selfPubKey { + allTrusted = append(allTrusted, n.PubKey()) + } + } + peers = allTrusted + } else { + peers = df.mainPeers + } + df.log.Debugf("updateDomainPeers: in fallback=%v mode will use %v nodes.", df.fallback, len(peers)) + df.dom.UpdatePeers(peers) +} + +func (df *DomainWithFallback) Attach(receiver byte, callback func(recv *peering.PeerMessageIn)) interface{} { + return df.dom.Attach(receiver, callback) +} + +func (df *DomainWithFallback) Detach(attachID interface{}) { + df.dom.Detach(attachID) +} + +func (df *DomainWithFallback) Close() { + df.dom.Close() + df.ctxCancel() +} + +func (df *DomainWithFallback) GetRandomOtherPeers(upToNumPeers int) []*ed25519.PublicKey { + return df.dom.GetRandomOtherPeers(upToNumPeers) +} + +func (df *DomainWithFallback) SendMsgByPubKey(pubKey *ed25519.PublicKey, msgReceiver, msgType byte, msgData []byte) { + df.dom.SendMsgByPubKey(pubKey, msgReceiver, msgType, msgData) +} diff --git a/packages/chain/statemgr/eventproc.go b/packages/chain/statemgr/eventproc.go index 8c30f49feb..a1b6d21f4c 100644 --- a/packages/chain/statemgr/eventproc.go +++ b/packages/chain/statemgr/eventproc.go @@ -46,7 +46,7 @@ func (sm *stateManager) handleGetBlockMsg(msg *messages.GetBlockMsgIn) { sm.log.Debugf("handleGetBlockMsg: responding to peer %s by block %v", msg.SenderPubKey.String(), msg.BlockIndex) blockMsg := &messages.BlockMsg{BlockBytes: blockBytes} - sm.chainPeers.SendMsgByPubKey(msg.SenderPubKey, peering.PeerMessageReceiverStateManager, peerMsgTypeBlock, util.MustBytes(blockMsg)) + sm.domain.SendMsgByPubKey(msg.SenderPubKey, peering.PeerMessageReceiverStateManager, peerMsgTypeBlock, util.MustBytes(blockMsg)) } // EventBlockMsg diff --git a/packages/chain/statemgr/setup_test.go b/packages/chain/statemgr/setup_test.go index feb11f560b..648106642c 100644 --- a/packages/chain/statemgr/setup_test.go +++ b/packages/chain/statemgr/setup_test.go @@ -190,6 +190,8 @@ func (env *MockedEnv) NewMockedNode(nodeIndex int, timers StateManagerTimers) *M log := env.Log.Named(nodePubKeyStr) peers, err := env.NetworkProviders[nodeIndex].PeerDomain(env.ChainID.Array(), env.NodePubKeys) require.NoError(env.T, err) + stateMgrDomain, err := NewDomainWithFallback(env.ChainID.Array(), env.NetworkProviders[nodeIndex], log) + require.NoError(env.T, err) ret := &MockedNode{ PubKey: nodePubKey, Env: env, @@ -234,7 +236,7 @@ func (env *MockedEnv) NewMockedNode(nodeIndex int, timers StateManagerTimers) *M }) } }) - ret.StateManager = New(ret.store, ret.ChainCore, ret.ChainPeers, ret.NodeConn, stateMgrMetrics, timers) + ret.StateManager = New(ret.store, ret.ChainCore, stateMgrDomain, ret.NodeConn, stateMgrMetrics, timers) ret.StateTransition = testchain.NewMockedStateTransition(env.T, env.OriginatorKeyPair) ret.StateTransition.OnNextState(func(vstate state.VirtualStateAccess, tx *ledgerstate.Transaction) { log.Debugf("MockedEnv.onNextState: state index %d", vstate.BlockIndex()) diff --git a/packages/chain/statemgr/statemgr.go b/packages/chain/statemgr/statemgr.go index 6eaaaaefce..481915f612 100644 --- a/packages/chain/statemgr/statemgr.go +++ b/packages/chain/statemgr/statemgr.go @@ -10,6 +10,7 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/chain" @@ -26,7 +27,7 @@ type stateManager struct { ready *ready.Ready store kvstore.KVStore chain chain.ChainCore - chainPeers peering.PeerDomainProvider + domain *DomainWithFallback nodeConn chain.ChainNodeConnection pullStateRetryTime time.Time solidState state.VirtualStateAccess @@ -60,7 +61,14 @@ const ( peerMsgTypeBlock ) -func New(store kvstore.KVStore, c chain.ChainCore, peers peering.PeerDomainProvider, nodeconn chain.ChainNodeConnection, stateManagerMetrics metrics.StateManagerMetrics, timersOpt ...StateManagerTimers) chain.StateManager { +func New( + store kvstore.KVStore, + c chain.ChainCore, + domain *DomainWithFallback, + nodeconn chain.ChainNodeConnection, + stateManagerMetrics metrics.StateManagerMetrics, + timersOpt ...StateManagerTimers, +) chain.StateManager { var timers StateManagerTimers if len(timersOpt) > 0 { timers = timersOpt[0] @@ -72,7 +80,7 @@ func New(store kvstore.KVStore, c chain.ChainCore, peers peering.PeerDomainProvi store: store, chain: c, nodeConn: nodeconn, - chainPeers: peers, + domain: domain, syncingBlocks: newSyncingBlocks(c.Log(), timers.GetBlockRetry), timers: timers, log: c.Log().Named("s"), @@ -85,7 +93,7 @@ func New(store kvstore.KVStore, c chain.ChainCore, peers peering.PeerDomainProvi eventTimerMsgPipe: pipe.NewLimitInfinitePipe(1), stateManagerMetrics: stateManagerMetrics, } - ret.receivePeerMessagesAttachID = ret.chainPeers.Attach(peering.PeerMessageReceiverStateManager, ret.receiveChainPeerMessages) + ret.receivePeerMessagesAttachID = ret.domain.Attach(peering.PeerMessageReceiverStateManager, ret.receiveChainPeerMessages) ret.nodeConn.AttachToOutputReceived(ret.EnqueueOutputMsg) go ret.initLoadState() @@ -119,9 +127,14 @@ func (sm *stateManager) receiveChainPeerMessages(peerMsg *peering.PeerMessageIn) } } +func (sm *stateManager) SetChainPeers(peers []*ed25519.PublicKey) { + sm.domain.SetMainPeers(peers) +} + func (sm *stateManager) Close() { sm.nodeConn.DetachFromOutputReceived() - sm.chainPeers.Detach(sm.receivePeerMessagesAttachID) + sm.domain.Detach(sm.receivePeerMessagesAttachID) + sm.domain.Close() sm.eventGetBlockMsgPipe.Close() sm.eventBlockMsgPipe.Close() diff --git a/packages/chain/statemgr/statemgr_test.go b/packages/chain/statemgr/statemgr_test.go index a39eae90a4..59878b6a6d 100644 --- a/packages/chain/statemgr/statemgr_test.go +++ b/packages/chain/statemgr/statemgr_test.go @@ -156,20 +156,24 @@ func TestManyStateTransitionsSeveralNodes(t *testing.T) { env, _ := NewMockedEnv(2, t, true) env.SetPushStateToNodesOption(true) - node := env.NewMockedNode(0, NewStateManagerTimers()) - node.StateManager.Ready().MustWait() - node.StartTimer() - - env.AddNode(node) + node0 := env.NewMockedNode(0, NewStateManagerTimers()) + node0.StateManager.Ready().MustWait() + node0.StartTimer() + node0.StateManager.SetChainPeers([]*ed25519.PublicKey{node0.PubKey}) + env.AddNode(node0) + env.Log.Infof("TestManyStateTransitionsSeveralNodes: node0.PubKey=%v", node0.PubKey.String()) const targetBlockIndex = 10 - node.OnStateTransitionMakeNewStateTransition(targetBlockIndex) - waitSyncBlockIndexAndCheck(10*time.Second, t, node, targetBlockIndex) + node0.OnStateTransitionMakeNewStateTransition(targetBlockIndex) + waitSyncBlockIndexAndCheck(10*time.Second, t, node0, targetBlockIndex) node1 := env.NewMockedNode(1, NewStateManagerTimers()) node1.StateManager.Ready().MustWait() node1.StartTimer() + node1.StateManager.SetChainPeers([]*ed25519.PublicKey{node0.PubKey, node1.PubKey}) + node0.StateManager.SetChainPeers([]*ed25519.PublicKey{node0.PubKey, node1.PubKey}) env.AddNode(node1) + env.Log.Infof("TestManyStateTransitionsSeveralNodes: node1.PubKey=%v", node1.PubKey.String()) waitSyncBlockIndexAndCheck(10*time.Second, t, node1, targetBlockIndex) } @@ -179,15 +183,19 @@ func TestManyStateTransitionsManyNodes(t *testing.T) { env, _ := NewMockedEnv(numberOfCatchingPeers+1, t, true) env.SetPushStateToNodesOption(true) - node := env.NewMockedNode(0, NewStateManagerTimers()) - node.StateManager.Ready().MustWait() - node.StartTimer() + allPubKeys := make([]*ed25519.PublicKey, 0) - env.AddNode(node) + node0 := env.NewMockedNode(0, NewStateManagerTimers()) + node0.StateManager.Ready().MustWait() + node0.StartTimer() + allPubKeys = append(allPubKeys, node0.PubKey) + + env.AddNode(node0) + node0.StateManager.SetChainPeers(allPubKeys) const targetBlockIndex = 5 - node.OnStateTransitionMakeNewStateTransition(targetBlockIndex) - waitSyncBlockIndexAndCheck(10*time.Second, t, node, targetBlockIndex) + node0.OnStateTransitionMakeNewStateTransition(targetBlockIndex) + waitSyncBlockIndexAndCheck(10*time.Second, t, node0, targetBlockIndex) catchingNodes := make([]*MockedNode, numberOfCatchingPeers) for i := 0; i < numberOfCatchingPeers; i++ { @@ -195,8 +203,11 @@ func TestManyStateTransitionsManyNodes(t *testing.T) { timers.GetBlockRetry = 200 * time.Millisecond catchingNodes[i] = env.NewMockedNode(i+1, timers) catchingNodes[i].StateManager.Ready().MustWait() + allPubKeys = append(allPubKeys, catchingNodes[i].PubKey) } + node0.StateManager.SetChainPeers(allPubKeys) for i := 0; i < numberOfCatchingPeers; i++ { + catchingNodes[i].StateManager.SetChainPeers(allPubKeys) catchingNodes[i].StartTimer() } for i := 0; i < numberOfCatchingPeers; i++ { @@ -213,20 +224,22 @@ func TestCatchUpNoConfirmedOutput(t *testing.T) { env, _ := NewMockedEnv(2, t, true) env.SetPushStateToNodesOption(true) - node := env.NewMockedNode(0, NewStateManagerTimers()) - node.StateManager.Ready().MustWait() - node.StartTimer() - - env.AddNode(node) + node0 := env.NewMockedNode(0, NewStateManagerTimers()) + node0.StateManager.Ready().MustWait() + node0.StartTimer() + node0.StateManager.SetChainPeers([]*ed25519.PublicKey{node0.PubKey}) + env.AddNode(node0) const targetBlockIndex = 10 - node.OnStateTransitionMakeNewStateTransition(targetBlockIndex) - node.NodeConn.OnPullConfirmedOutput(func(outputID ledgerstate.OutputID) {}) - waitSyncBlockIndexAndCheck(10*time.Second, t, node, targetBlockIndex) + node0.OnStateTransitionMakeNewStateTransition(targetBlockIndex) + node0.NodeConn.OnPullConfirmedOutput(func(outputID ledgerstate.OutputID) {}) + waitSyncBlockIndexAndCheck(10*time.Second, t, node0, targetBlockIndex) node1 := env.NewMockedNode(1, NewStateManagerTimers()) node1.StateManager.Ready().MustWait() node1.StartTimer() + node1.StateManager.SetChainPeers([]*ed25519.PublicKey{node0.PubKey, node1.PubKey}) + node0.StateManager.SetChainPeers([]*ed25519.PublicKey{node0.PubKey, node1.PubKey}) env.AddNode(node1) waitSyncBlockIndexAndCheck(10*time.Second, t, node1, targetBlockIndex) @@ -334,6 +347,7 @@ func TestCruelWorld(t *testing.T) { nodes[i] = env.NewMockedNode(i, timers) nodes[i].StateManager.Ready().MustWait() nodes[i].StartTimer() + nodes[i].StateManager.SetChainPeers(env.NodePubKeys) env.AddNode(nodes[i]) } diff --git a/packages/chain/statemgr/syncmgr.go b/packages/chain/statemgr/syncmgr.go index aa0d023c94..18e911ca25 100644 --- a/packages/chain/statemgr/syncmgr.go +++ b/packages/chain/statemgr/syncmgr.go @@ -64,6 +64,9 @@ func (sm *stateManager) doSyncActionIfNeeded() { switch { case sm.solidState.BlockIndex() == sm.stateOutput.GetStateIndex(): sm.log.Debugf("doSyncAction not needed: state is already synced at index #%d", sm.stateOutput.GetStateIndex()) + if sm.domain.HaveMainPeers() { + sm.domain.SetFallbackMode(false) + } return case sm.solidState.BlockIndex() > sm.stateOutput.GetStateIndex(): sm.log.Debugf("BlockIndex=%v, StateIndex=%v", sm.solidState.BlockIndex(), sm.stateOutput.GetStateIndex()) @@ -72,6 +75,14 @@ func (sm *stateManager) doSyncActionIfNeeded() { // not synced startSyncFromIndex := sm.solidState.BlockIndex() + 1 sm.log.Debugf("doSyncAction: trying to sync state from index %v to %v", startSyncFromIndex, sm.stateOutput.GetStateIndex()) + if !sm.domain.HaveMainPeers() { + // + // + // TODO: XXX: KP: Other conditions... + // + // + sm.domain.SetFallbackMode(true) + } for i := startSyncFromIndex; i <= sm.stateOutput.GetStateIndex(); i++ { requestBlockRetryTime := sm.syncingBlocks.getRequestBlockRetryTime(i) blockCandidatesCount := sm.syncingBlocks.getBlockCandidatesCount(i) @@ -86,9 +97,12 @@ func (sm *stateManager) doSyncActionIfNeeded() { nowis := time.Now() if nowis.After(requestBlockRetryTime) { // have to pull - sm.log.Debugf("doSyncAction: requesting block index %v from %v random peers", i, numberOfNodesToRequestBlockFromConst) + sm.log.Debugf("doSyncAction: requesting block index %v, fallback=%v from %v random peers.", i, sm.domain.GetFallbackMode(), numberOfNodesToRequestBlockFromConst) getBlockMsg := &messages.GetBlockMsg{BlockIndex: i} - sm.chainPeers.SendPeerMsgToRandomPeers(numberOfNodesToRequestBlockFromConst, peering.PeerMessageReceiverStateManager, peerMsgTypeGetBlock, util.MustBytes(getBlockMsg)) + for _, p := range sm.domain.GetRandomOtherPeers(numberOfNodesToRequestBlockFromConst) { + sm.domain.SendMsgByPubKey(p, peering.PeerMessageReceiverStateManager, peerMsgTypeGetBlock, util.MustBytes(getBlockMsg)) + sm.log.Debugf("doSyncAction: requesting block index %v,from %v", i, p.String()) + } sm.syncingBlocks.startSyncingIfNeeded(i) sm.syncingBlocks.setRequestBlockRetryTime(i, nowis.Add(sm.timers.GetBlockRetry)) if blockCandidatesCount == 0 { diff --git a/packages/peering/domain/domain.go b/packages/peering/domain/domain.go index ccb4b28806..005068cdbd 100644 --- a/packages/peering/domain/domain.go +++ b/packages/peering/domain/domain.go @@ -31,16 +31,15 @@ func NewPeerDomain(netProvider peering.NetworkProvider, peeringID peering.Peerin ret := &DomainImpl{ netProvider: netProvider, nodes: make(map[ed25519.PublicKey]peering.PeerSender), - permutation: util.NewPermutation16(uint16(len(initialNodes)), nil), - permPubKeys: make([]*ed25519.PublicKey, len(initialNodes)), + permutation: nil, // Will be set in ret.reshufflePeers(). + permPubKeys: nil, // Will be set in ret.reshufflePeers(). peeringID: peeringID, attachIDs: make([]interface{}, 0), log: log, mutex: &sync.RWMutex{}, } - for i, sender := range initialNodes { + for _, sender := range initialNodes { ret.nodes[*sender.PubKey()] = sender - ret.permPubKeys[i] = sender.PubKey() } ret.reshufflePeers() return ret @@ -62,13 +61,7 @@ func (d *DomainImpl) SendMsgByPubKey(pubKey *ed25519.PublicKey, msgReceiver, msg }) } -func (d *DomainImpl) SendPeerMsgToRandomPeers(upToNumPeers int, msgReceiver, msgType byte, msgData []byte) { - for _, pubKey := range d.GetRandomPeers(upToNumPeers) { - d.SendMsgByPubKey(pubKey, msgReceiver, msgType, msgData) - } -} - -func (d *DomainImpl) GetRandomPeers(upToNumPeers int) []*ed25519.PublicKey { +func (d *DomainImpl) GetRandomOtherPeers(upToNumPeers int) []*ed25519.PublicKey { d.mutex.RLock() defer d.mutex.RUnlock() if upToNumPeers > len(d.permPubKeys) { @@ -130,7 +123,7 @@ func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { if changed { d.mutex.Lock() d.nodes = nodes - d.permutation = util.NewPermutation16(uint16(len(nodes)), nil) + d.reshufflePeers() d.mutex.Unlock() } } @@ -145,7 +138,9 @@ func (d *DomainImpl) reshufflePeers(seedBytes ...[]byte) { d.permPubKeys = make([]*ed25519.PublicKey, 0, len(d.nodes)) for pubKey := range d.nodes { peerPubKey := pubKey - d.permPubKeys = append(d.permPubKeys, &peerPubKey) + if peerPubKey != *d.netProvider.Self().PubKey() { // Do not include self to the permutation. + d.permPubKeys = append(d.permPubKeys, &peerPubKey) + } } var seedB []byte if len(seedBytes) == 0 { @@ -155,7 +150,7 @@ func (d *DomainImpl) reshufflePeers(seedBytes ...[]byte) { } else { seedB = seedBytes[0] } - d.permutation.Shuffle(seedB) + d.permutation = util.NewPermutation16(uint16(len(d.permPubKeys)), seedB) } func (d *DomainImpl) Attach(receiver byte, callback func(recv *peering.PeerMessageIn)) interface{} { diff --git a/packages/peering/domain/domain_test.go b/packages/peering/domain/domain_test.go index bc890a6e85..34ea878012 100644 --- a/packages/peering/domain/domain_test.go +++ b/packages/peering/domain/domain_test.go @@ -107,10 +107,10 @@ func TestRandom(t *testing.T) { for i := 0; i < 5; i++ { wg.Add(sendTo * 2) t.Log("----------------------------------") - for _, pubKey := range d1.GetRandomPeers(sendTo) { + for _, pubKey := range d1.GetRandomOtherPeers(sendTo) { d1.SendMsgByPubKey(pubKey, receiver, 125, []byte{}) } - for _, pubKey := range d2.GetRandomPeers(sendTo) { + for _, pubKey := range d2.GetRandomOtherPeers(sendTo) { d2.SendMsgByPubKey(pubKey, receiver, 125, []byte{}) } wg.Wait() diff --git a/packages/peering/peering.go b/packages/peering/peering.go index 999ca96903..2bb7af01d6 100644 --- a/packages/peering/peering.go +++ b/packages/peering/peering.go @@ -90,12 +90,11 @@ type GroupProvider interface { // All peers in the domain shares same peeringID. Each peer within domain is identified via its netID type PeerDomainProvider interface { ReshufflePeers(seedBytes ...[]byte) - GetRandomPeers(upToNumPeers int) []*ed25519.PublicKey + GetRandomOtherPeers(upToNumPeers int) []*ed25519.PublicKey UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) Attach(receiver byte, callback func(recv *PeerMessageIn)) interface{} Detach(attachID interface{}) SendMsgByPubKey(pubKey *ed25519.PublicKey, msgReceiver byte, msgType byte, msgData []byte) - SendPeerMsgToRandomPeers(upToNumPeers int, msgReceiver byte, msgType byte, msgData []byte) PeerStatus() []PeerStatusProvider Close() } diff --git a/packages/testutil/trustedNetworkManager.go b/packages/testutil/trustedNetworkManager.go index 0ba5512b74..1eb46ba277 100644 --- a/packages/testutil/trustedNetworkManager.go +++ b/packages/testutil/trustedNetworkManager.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package testutil import ( diff --git a/tools/cluster/tests/advanced_inccounter_test.go b/tools/cluster/tests/advanced_inccounter_test.go index 4ba8914d1f..72aea340e3 100644 --- a/tools/cluster/tests/advanced_inccounter_test.go +++ b/tools/cluster/tests/advanced_inccounter_test.go @@ -122,9 +122,13 @@ func testAccessNodesOnLedger(t *testing.T, numRequests, numValidatorNodes, clust require.NoError(t, err) } - waitUntil(t, e.counterEquals(int64(numRequests)), util.MakeRange(0, clusterSize), 40*time.Second) + waitUntil(t, e.counterEquals(int64(numRequests)), util.MakeRange(0, clusterSize), 40*time.Second, "a required number of testAccessNodesOnLedger requests") - e.printBlocks(numRequests + 3) + e.printBlocks( + numRequests + // The actual IncCounter requests. + 3 + // Initial State + IncCounter SC Deploy + ??? + (clusterSize - numValidatorNodes), // Access node applications. + ) } func TestAccessNodesOffLedger(t *testing.T) { @@ -215,7 +219,11 @@ func testAccessNodesOffLedger(t *testing.T, numRequests, numValidatorNodes, clus waitUntil(t, e.counterEquals(int64(numRequests)), util.MakeRange(0, clusterSize), to, "requests counted") - e.printBlocks(numRequests + 4) + e.printBlocks( + numRequests + // The actual IncCounter requests. + 4 + // ??? + (clusterSize - numValidatorNodes), // Access nodes applications. + ) } // extreme test @@ -248,7 +256,11 @@ func TestAccessNodesMany(t *testing.T) { waitUntil(t, e.counterEquals(int64(requestsCumulative)), e.clu.Config.AllNodes(), 60*time.Second, logMsg) requestsCount *= requestsCountProgression } - e.printBlocks(posted + 3) + e.printBlocks( + posted + // The actual SC requests. + 3 + // ??? + (clusterSize - numValidatorNodes), // GOV: Access Node Applications. + ) } // cluster of 10 access nodes and two overlapping committees From 819f671fc690079f7c901d2cac522882128e6761 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 5 Jan 2022 14:44:32 +0200 Subject: [PATCH 052/120] Rotation tests are passing. --- packages/chain/chainimpl/eventproc.go | 5 ++--- .../core/governance/governanceimpl/statecontroller.go | 1 + packages/webapi/admapi/chainrecord.go | 5 ++++- tools/cluster/tests/advanced_inccounter_test.go | 10 ++++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/chain/chainimpl/eventproc.go b/packages/chain/chainimpl/eventproc.go index 1eb70a4939..3f434ca00a 100644 --- a/packages/chain/chainimpl/eventproc.go +++ b/packages/chain/chainimpl/eventproc.go @@ -142,10 +142,9 @@ func (c *chainObj) rotateCommitteeIfNeeded(anchorOutput *ledgerstate.AliasOutput } dkShare, err := c.getChainDKShare(anchorOutput.GetStateAddress()) if err != nil { - if errors.Is(err, registry.ErrDKShareNotFound) { - return nil + if !errors.Is(err, registry.ErrDKShareNotFound) { + return xerrors.Errorf("rotateCommitteeIfNeeded: unable to load dkShare: %w", err) } - return xerrors.Errorf("rotateCommitteeIfNeeded: unable to load dkShare: %w", err) } // rotation needed // close current in any case diff --git a/packages/vm/core/governance/governanceimpl/statecontroller.go b/packages/vm/core/governance/governanceimpl/statecontroller.go index 88c6e820e0..7f9f53a96a 100644 --- a/packages/vm/core/governance/governanceimpl/statecontroller.go +++ b/packages/vm/core/governance/governanceimpl/statecontroller.go @@ -33,6 +33,7 @@ func rotateStateController(ctx iscp.Sandbox) (dict.Dict, error) { // rotate request to another address has been issued. State update will be taken over by VM and will have no effect // By setting StateVarRotateToAddress we signal the VM this special situation // StateVarRotateToAddress value should never persist in the state + ctx.Log().Infof("Governance::RotateStateController: newStateControllerAddress=%s", newStateControllerAddr.String()) ctx.State().Set(governance.StateVarRotateToAddress, newStateControllerAddr.Bytes()) return nil, nil } diff --git a/packages/webapi/admapi/chainrecord.go b/packages/webapi/admapi/chainrecord.go index 3695386308..3abe182b86 100644 --- a/packages/webapi/admapi/chainrecord.go +++ b/packages/webapi/admapi/chainrecord.go @@ -55,7 +55,10 @@ func (s *chainRecordService) handlePutChainRecord(c echo.Context) error { return err } if bd2 != nil { - return httperrors.Conflict(fmt.Sprintf("Record already exists: %s", bd.ChainID.String())) + // Make this call idempotent. + // Record has no information apart from the ChainID and activation status. + // So just keep the existing, if it exists. + return c.NoContent(http.StatusCreated) } if err := reg.SaveChainRecord(bd); err != nil { return err diff --git a/tools/cluster/tests/advanced_inccounter_test.go b/tools/cluster/tests/advanced_inccounter_test.go index 72aea340e3..c11ee7f96c 100644 --- a/tools/cluster/tests/advanced_inccounter_test.go +++ b/tools/cluster/tests/advanced_inccounter_test.go @@ -282,6 +282,11 @@ func TestRotation(t *testing.T) { chain, err := clu.DeployChain("chain", clu.Config.AllNodes(), cmt1, 3, addr1) require.NoError(t, err) t.Logf("chainID: %s", chain.ChainID.Base58()) + for _, i := range cmt1 { + // Add the first committee as access nodes as well, + // because they will be such after the committee rotation. + require.NoError(t, clu.AddAccessNode(i, chain)) + } description := "inccounter testing contract" programHash := inccounter.Contract.ProgramHash @@ -388,6 +393,11 @@ func TestRotationMany(t *testing.T) { chain, err := clu.DeployChain("chain", clu.Config.AllNodes(), cmt[0], quorum[0], addrs[0]) require.NoError(t, err) t.Logf("chainID: %s", chain.ChainID.Base58()) + for _, i := range cmt[0] { + // Add the first committee as access nodes as well, + // because they will be such after the committee rotation. + require.NoError(t, clu.AddAccessNode(i, chain)) + } govClient := chain.SCClient(governance.Contract.Hname(), chain.OriginatorKeyPair()) From 6cdede3c963f2a472de9fbc94adf66873bb15448 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 5 Jan 2022 15:00:22 +0200 Subject: [PATCH 053/120] Do not report duplicates in tests. --- packages/testutil/peeringNetBehaviourDynamic_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/testutil/peeringNetBehaviourDynamic_test.go b/packages/testutil/peeringNetBehaviourDynamic_test.go index d54c9aba1f..f8504f8ce8 100644 --- a/packages/testutil/peeringNetBehaviourDynamic_test.go +++ b/packages/testutil/peeringNetBehaviourDynamic_test.go @@ -142,7 +142,7 @@ func TestPeeringNetDynamicChanging(t *testing.T) { behavior.Close() } -func TestPeeringNetDynamicLosingChannel(t *testing.T) { +func TestPeeringNetDynamicLosingChannel(t *testing.T) { //nolint:dupl inCh := make(chan *peeringMsg) outCh := make(chan *peeringMsg, 1000) stopCh := make(chan bool) @@ -167,7 +167,7 @@ func TestPeeringNetDynamicLosingChannel(t *testing.T) { behavior.Close() } -func TestPeeringNetDynamicRepeatingChannel(t *testing.T) { +func TestPeeringNetDynamicRepeatingChannel(t *testing.T) { //nolint:dupl inCh := make(chan *peeringMsg) outCh := make(chan *peeringMsg, 10000) stopCh := make(chan bool) From 354b9a81ffeba35cda33ea3f667a77f03be3c062 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 5 Jan 2022 07:37:05 -0800 Subject: [PATCH 054/120] Added AsImmutable() to mutable state proxy --- contracts/wasm/dividend/go/dividend/state.go | 4 + contracts/wasm/dividend/src/state.rs | 4 + contracts/wasm/dividend/ts/dividend/state.ts | 6 + .../go/donatewithfeedback/state.go | 4 + .../wasm/donatewithfeedback/src/state.rs | 4 + .../ts/donatewithfeedback/state.ts | 6 + contracts/wasm/erc20/go/erc20/state.go | 4 + contracts/wasm/erc20/src/state.rs | 4 + contracts/wasm/erc20/ts/erc20/state.ts | 6 + contracts/wasm/erc721/go/erc721/state.go | 4 + contracts/wasm/erc721/src/state.rs | 4 + contracts/wasm/erc721/ts/erc721/state.ts | 6 + .../wasm/fairauction/go/fairauction/state.go | 4 + contracts/wasm/fairauction/src/state.rs | 4 + .../wasm/fairauction/ts/fairauction/state.ts | 6 + .../fairroulette/go/fairroulette/state.go | 4 + contracts/wasm/fairroulette/src/state.rs | 4 + .../fairroulette/ts/fairroulette/state.ts | 6 + .../wasm/helloworld/go/helloworld/state.go | 4 + .../wasm/helloworld/ts/helloworld/state.ts | 5 + .../wasm/inccounter/go/inccounter/state.go | 4 + contracts/wasm/inccounter/src/state.rs | 4 + .../wasm/inccounter/ts/inccounter/state.ts | 6 + contracts/wasm/testcore/go/testcore/state.go | 4 + contracts/wasm/testcore/src/state.rs | 4 + contracts/wasm/testcore/ts/testcore/state.ts | 6 + .../wasm/testwasmlib/go/testwasmlib/state.go | 4 + .../go/testwasmlibclient/events.go | 28 ++ .../go/testwasmlibclient/service.go | 428 ++++++++++++++++++ .../go/testwasmlibclient/testwasmlib.go | 7 + contracts/wasm/testwasmlib/src/state.rs | 4 + .../test/testwasmlib_client_test.go | 116 +++++ .../wasm/testwasmlib/ts/testwasmlib/state.ts | 6 + .../ts/testwasmlibclient/events.ts | 24 + .../testwasmlib/ts/testwasmlibclient/index.ts | 10 + .../ts/testwasmlibclient/service.ts | 398 ++++++++++++++++ .../ts/testwasmlibclient/testwasmlib.ts | 8 + .../ts/testwasmlibclient/tsconfig.json | 11 + .../wasm/timestamp/go/timestamp/state.go | 4 + contracts/wasm/timestamp/src/state.rs | 4 + .../wasm/timestamp/ts/timestamp/state.ts | 6 + .../tokenregistry/go/tokenregistry/state.go | 4 + contracts/wasm/tokenregistry/src/state.rs | 4 + .../tokenregistry/ts/tokenregistry/state.ts | 6 + tools/schema/generator/gotemplates/state.go | 8 + tools/schema/generator/rstemplates/state.go | 8 + tools/schema/generator/tstemplates/state.go | 10 + 47 files changed, 1219 insertions(+) create mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/events.go create mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/service.go create mode 100644 contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go create mode 100644 contracts/wasm/testwasmlib/test/testwasmlib_client_test.go create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts create mode 100644 contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json diff --git a/contracts/wasm/dividend/go/dividend/state.go b/contracts/wasm/dividend/go/dividend/state.go index 91571d4443..3bf445ac89 100644 --- a/contracts/wasm/dividend/go/dividend/state.go +++ b/contracts/wasm/dividend/go/dividend/state.go @@ -83,6 +83,10 @@ type MutableDividendState struct { id int32 } +func (s MutableDividendState) AsImmutable() ImmutableDividendState { + return ImmutableDividendState(s) +} + func (s MutableDividendState) MemberList() ArrayOfMutableAddress { arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS) return ArrayOfMutableAddress{objID: arrID} diff --git a/contracts/wasm/dividend/src/state.rs b/contracts/wasm/dividend/src/state.rs index 351caa28fa..70180a46d7 100644 --- a/contracts/wasm/dividend/src/state.rs +++ b/contracts/wasm/dividend/src/state.rs @@ -105,6 +105,10 @@ pub struct MutableDividendState { } impl MutableDividendState { + pub fn as_immutable(&self) -> ImmutableDividendState { + ImmutableDividendState { id: self.id } + } + pub fn member_list(&self) -> ArrayOfMutableAddress { let arr_id = get_object_id(self.id, idx_map(IDX_STATE_MEMBER_LIST), TYPE_ARRAY | TYPE_ADDRESS); ArrayOfMutableAddress { obj_id: arr_id } diff --git a/contracts/wasm/dividend/ts/dividend/state.ts b/contracts/wasm/dividend/ts/dividend/state.ts index b7a24f7015..491a211f4e 100644 --- a/contracts/wasm/dividend/ts/dividend/state.ts +++ b/contracts/wasm/dividend/ts/dividend/state.ts @@ -93,6 +93,12 @@ export class MapAddressToMutableInt64 { } export class MutableDividendState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableDividendState { + const imm = new sc.ImmutableDividendState(); + imm.mapID = this.mapID; + return imm; + } + memberList(): sc.ArrayOfMutableAddress { let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS); return new sc.ArrayOfMutableAddress(arrID); diff --git a/contracts/wasm/donatewithfeedback/go/donatewithfeedback/state.go b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/state.go index 9cb723cdac..9f88c3e5df 100644 --- a/contracts/wasm/donatewithfeedback/go/donatewithfeedback/state.go +++ b/contracts/wasm/donatewithfeedback/go/donatewithfeedback/state.go @@ -58,6 +58,10 @@ type MutableDonateWithFeedbackState struct { id int32 } +func (s MutableDonateWithFeedbackState) AsImmutable() ImmutableDonateWithFeedbackState { + return ImmutableDonateWithFeedbackState(s) +} + func (s MutableDonateWithFeedbackState) Log() ArrayOfMutableDonation { arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateLog], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) return ArrayOfMutableDonation{objID: arrID} diff --git a/contracts/wasm/donatewithfeedback/src/state.rs b/contracts/wasm/donatewithfeedback/src/state.rs index d7ef3fcf92..8834e2bf04 100644 --- a/contracts/wasm/donatewithfeedback/src/state.rs +++ b/contracts/wasm/donatewithfeedback/src/state.rs @@ -75,6 +75,10 @@ pub struct MutableDonateWithFeedbackState { } impl MutableDonateWithFeedbackState { + pub fn as_immutable(&self) -> ImmutableDonateWithFeedbackState { + ImmutableDonateWithFeedbackState { id: self.id } + } + pub fn log(&self) -> ArrayOfMutableDonation { let arr_id = get_object_id(self.id, idx_map(IDX_STATE_LOG), TYPE_ARRAY | TYPE_BYTES); ArrayOfMutableDonation { obj_id: arr_id } diff --git a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/state.ts b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/state.ts index 0a946bfcf1..88c74bcc8e 100644 --- a/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/state.ts +++ b/contracts/wasm/donatewithfeedback/ts/donatewithfeedback/state.ts @@ -60,6 +60,12 @@ export class ArrayOfMutableDonation { } export class MutableDonateWithFeedbackState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableDonateWithFeedbackState { + const imm = new sc.ImmutableDonateWithFeedbackState(); + imm.mapID = this.mapID; + return imm; + } + log(): sc.ArrayOfMutableDonation { let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateLog], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); return new sc.ArrayOfMutableDonation(arrID); diff --git a/contracts/wasm/erc20/go/erc20/state.go b/contracts/wasm/erc20/go/erc20/state.go index 8736cc54c3..1a0a531ffc 100644 --- a/contracts/wasm/erc20/go/erc20/state.go +++ b/contracts/wasm/erc20/go/erc20/state.go @@ -53,6 +53,10 @@ type MutableErc20State struct { id int32 } +func (s MutableErc20State) AsImmutable() ImmutableErc20State { + return ImmutableErc20State(s) +} + func (s MutableErc20State) AllAllowances() MapAgentIDToMutableAllowancesForAgent { mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateAllAllowances], wasmlib.TYPE_MAP) return MapAgentIDToMutableAllowancesForAgent{objID: mapID} diff --git a/contracts/wasm/erc20/src/state.rs b/contracts/wasm/erc20/src/state.rs index 2549d40de5..6cd4cf35e8 100644 --- a/contracts/wasm/erc20/src/state.rs +++ b/contracts/wasm/erc20/src/state.rs @@ -70,6 +70,10 @@ pub struct MutableErc20State { } impl MutableErc20State { + pub fn as_immutable(&self) -> ImmutableErc20State { + ImmutableErc20State { id: self.id } + } + pub fn all_allowances(&self) -> MapAgentIDToMutableAllowancesForAgent { let map_id = get_object_id(self.id, idx_map(IDX_STATE_ALL_ALLOWANCES), TYPE_MAP); MapAgentIDToMutableAllowancesForAgent { obj_id: map_id } diff --git a/contracts/wasm/erc20/ts/erc20/state.ts b/contracts/wasm/erc20/ts/erc20/state.ts index 132fad2c0b..27ce1e622c 100644 --- a/contracts/wasm/erc20/ts/erc20/state.ts +++ b/contracts/wasm/erc20/ts/erc20/state.ts @@ -55,6 +55,12 @@ export class MapAgentIDToMutableAllowancesForAgent { } export class MutableErc20State extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableErc20State { + const imm = new sc.ImmutableErc20State(); + imm.mapID = this.mapID; + return imm; + } + allAllowances(): sc.MapAgentIDToMutableAllowancesForAgent { let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateAllAllowances], wasmlib.TYPE_MAP); return new sc.MapAgentIDToMutableAllowancesForAgent(mapID); diff --git a/contracts/wasm/erc721/go/erc721/state.go b/contracts/wasm/erc721/go/erc721/state.go index c3ad62781b..57a6e0ccbb 100644 --- a/contracts/wasm/erc721/go/erc721/state.go +++ b/contracts/wasm/erc721/go/erc721/state.go @@ -107,6 +107,10 @@ type MutableErc721State struct { id int32 } +func (s MutableErc721State) AsImmutable() ImmutableErc721State { + return ImmutableErc721State(s) +} + func (s MutableErc721State) ApprovedAccounts() MapHashToMutableAgentID { mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateApprovedAccounts], wasmlib.TYPE_MAP) return MapHashToMutableAgentID{objID: mapID} diff --git a/contracts/wasm/erc721/src/state.rs b/contracts/wasm/erc721/src/state.rs index ae8cd1e68a..f48fc5e9cb 100644 --- a/contracts/wasm/erc721/src/state.rs +++ b/contracts/wasm/erc721/src/state.rs @@ -136,6 +136,10 @@ pub struct MutableErc721State { } impl MutableErc721State { + pub fn as_immutable(&self) -> ImmutableErc721State { + ImmutableErc721State { id: self.id } + } + pub fn approved_accounts(&self) -> MapHashToMutableAgentID { let map_id = get_object_id(self.id, idx_map(IDX_STATE_APPROVED_ACCOUNTS), TYPE_MAP); MapHashToMutableAgentID { obj_id: map_id } diff --git a/contracts/wasm/erc721/ts/erc721/state.ts b/contracts/wasm/erc721/ts/erc721/state.ts index d85b7c9295..247afa6dc6 100644 --- a/contracts/wasm/erc721/ts/erc721/state.ts +++ b/contracts/wasm/erc721/ts/erc721/state.ts @@ -125,6 +125,12 @@ export class MapAgentIDToMutableUint64 { } export class MutableErc721State extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableErc721State { + const imm = new sc.ImmutableErc721State(); + imm.mapID = this.mapID; + return imm; + } + approvedAccounts(): sc.MapHashToMutableAgentID { let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateApprovedAccounts], wasmlib.TYPE_MAP); return new sc.MapHashToMutableAgentID(mapID); diff --git a/contracts/wasm/fairauction/go/fairauction/state.go b/contracts/wasm/fairauction/go/fairauction/state.go index aa6ae8655b..fc21692cc0 100644 --- a/contracts/wasm/fairauction/go/fairauction/state.go +++ b/contracts/wasm/fairauction/go/fairauction/state.go @@ -100,6 +100,10 @@ type MutableFairAuctionState struct { id int32 } +func (s MutableFairAuctionState) AsImmutable() ImmutableFairAuctionState { + return ImmutableFairAuctionState(s) +} + func (s MutableFairAuctionState) Auctions() MapColorToMutableAuction { mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateAuctions], wasmlib.TYPE_MAP) return MapColorToMutableAuction{objID: mapID} diff --git a/contracts/wasm/fairauction/src/state.rs b/contracts/wasm/fairauction/src/state.rs index 8e35dbc21b..180b7ac68c 100644 --- a/contracts/wasm/fairauction/src/state.rs +++ b/contracts/wasm/fairauction/src/state.rs @@ -130,6 +130,10 @@ pub struct MutableFairAuctionState { } impl MutableFairAuctionState { + pub fn as_immutable(&self) -> ImmutableFairAuctionState { + ImmutableFairAuctionState { id: self.id } + } + pub fn auctions(&self) -> MapColorToMutableAuction { let map_id = get_object_id(self.id, idx_map(IDX_STATE_AUCTIONS), TYPE_MAP); MapColorToMutableAuction { obj_id: map_id } diff --git a/contracts/wasm/fairauction/ts/fairauction/state.ts b/contracts/wasm/fairauction/ts/fairauction/state.ts index cc35e60b89..d9830861c1 100644 --- a/contracts/wasm/fairauction/ts/fairauction/state.ts +++ b/contracts/wasm/fairauction/ts/fairauction/state.ts @@ -118,6 +118,12 @@ export class MapColorToMutableBids { } export class MutableFairAuctionState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableFairAuctionState { + const imm = new sc.ImmutableFairAuctionState(); + imm.mapID = this.mapID; + return imm; + } + auctions(): sc.MapColorToMutableAuction { let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateAuctions], wasmlib.TYPE_MAP); return new sc.MapColorToMutableAuction(mapID); diff --git a/contracts/wasm/fairroulette/go/fairroulette/state.go b/contracts/wasm/fairroulette/go/fairroulette/state.go index f69f330f7e..06c34952c0 100644 --- a/contracts/wasm/fairroulette/go/fairroulette/state.go +++ b/contracts/wasm/fairroulette/go/fairroulette/state.go @@ -70,6 +70,10 @@ type MutableFairRouletteState struct { id int32 } +func (s MutableFairRouletteState) AsImmutable() ImmutableFairRouletteState { + return ImmutableFairRouletteState(s) +} + func (s MutableFairRouletteState) Bets() ArrayOfMutableBet { arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) return ArrayOfMutableBet{objID: arrID} diff --git a/contracts/wasm/fairroulette/src/state.rs b/contracts/wasm/fairroulette/src/state.rs index a9dab54565..72bb5ddf5a 100644 --- a/contracts/wasm/fairroulette/src/state.rs +++ b/contracts/wasm/fairroulette/src/state.rs @@ -87,6 +87,10 @@ pub struct MutableFairRouletteState { } impl MutableFairRouletteState { + pub fn as_immutable(&self) -> ImmutableFairRouletteState { + ImmutableFairRouletteState { id: self.id } + } + pub fn bets(&self) -> ArrayOfMutableBet { let arr_id = get_object_id(self.id, idx_map(IDX_STATE_BETS), TYPE_ARRAY | TYPE_BYTES); ArrayOfMutableBet { obj_id: arr_id } diff --git a/contracts/wasm/fairroulette/ts/fairroulette/state.ts b/contracts/wasm/fairroulette/ts/fairroulette/state.ts index 08059c1fc0..86d57a8f5f 100644 --- a/contracts/wasm/fairroulette/ts/fairroulette/state.ts +++ b/contracts/wasm/fairroulette/ts/fairroulette/state.ts @@ -72,6 +72,12 @@ export class ArrayOfMutableBet { } export class MutableFairRouletteState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableFairRouletteState { + const imm = new sc.ImmutableFairRouletteState(); + imm.mapID = this.mapID; + return imm; + } + bets(): sc.ArrayOfMutableBet { let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES); return new sc.ArrayOfMutableBet(arrID); diff --git a/contracts/wasm/helloworld/go/helloworld/state.go b/contracts/wasm/helloworld/go/helloworld/state.go index 2868513d3d..0b1fb82fcb 100644 --- a/contracts/wasm/helloworld/go/helloworld/state.go +++ b/contracts/wasm/helloworld/go/helloworld/state.go @@ -14,3 +14,7 @@ type ImmutableHelloWorldState struct { type MutableHelloWorldState struct { id int32 } + +func (s MutableHelloWorldState) AsImmutable() ImmutableHelloWorldState { + return ImmutableHelloWorldState(s) +} diff --git a/contracts/wasm/helloworld/ts/helloworld/state.ts b/contracts/wasm/helloworld/ts/helloworld/state.ts index d8026bdce6..b1476bca64 100644 --- a/contracts/wasm/helloworld/ts/helloworld/state.ts +++ b/contracts/wasm/helloworld/ts/helloworld/state.ts @@ -12,4 +12,9 @@ export class ImmutableHelloWorldState extends wasmlib.ScMapID { } export class MutableHelloWorldState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableHelloWorldState { + const imm = new sc.ImmutableHelloWorldState(); + imm.mapID = this.mapID; + return imm; + } } diff --git a/contracts/wasm/inccounter/go/inccounter/state.go b/contracts/wasm/inccounter/go/inccounter/state.go index 0795d48060..9d3ddb0b6b 100644 --- a/contracts/wasm/inccounter/go/inccounter/state.go +++ b/contracts/wasm/inccounter/go/inccounter/state.go @@ -25,6 +25,10 @@ type MutableIncCounterState struct { id int32 } +func (s MutableIncCounterState) AsImmutable() ImmutableIncCounterState { + return ImmutableIncCounterState(s) +} + func (s MutableIncCounterState) Counter() wasmlib.ScMutableInt64 { return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateCounter]) } diff --git a/contracts/wasm/inccounter/src/state.rs b/contracts/wasm/inccounter/src/state.rs index 36d65f563f..c37fa239da 100644 --- a/contracts/wasm/inccounter/src/state.rs +++ b/contracts/wasm/inccounter/src/state.rs @@ -35,6 +35,10 @@ pub struct MutableIncCounterState { } impl MutableIncCounterState { + pub fn as_immutable(&self) -> ImmutableIncCounterState { + ImmutableIncCounterState { id: self.id } + } + pub fn counter(&self) -> ScMutableInt64 { ScMutableInt64::new(self.id, idx_map(IDX_STATE_COUNTER)) } diff --git a/contracts/wasm/inccounter/ts/inccounter/state.ts b/contracts/wasm/inccounter/ts/inccounter/state.ts index d292899376..84bf572918 100644 --- a/contracts/wasm/inccounter/ts/inccounter/state.ts +++ b/contracts/wasm/inccounter/ts/inccounter/state.ts @@ -19,6 +19,12 @@ export class ImmutableIncCounterState extends wasmlib.ScMapID { } export class MutableIncCounterState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableIncCounterState { + const imm = new sc.ImmutableIncCounterState(); + imm.mapID = this.mapID; + return imm; + } + counter(): wasmlib.ScMutableInt64 { return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateCounter]); } diff --git a/contracts/wasm/testcore/go/testcore/state.go b/contracts/wasm/testcore/go/testcore/state.go index d0ccc022f7..e9f861dc56 100644 --- a/contracts/wasm/testcore/go/testcore/state.go +++ b/contracts/wasm/testcore/go/testcore/state.go @@ -38,6 +38,10 @@ type MutableTestCoreState struct { id int32 } +func (s MutableTestCoreState) AsImmutable() ImmutableTestCoreState { + return ImmutableTestCoreState(s) +} + func (s MutableTestCoreState) Counter() wasmlib.ScMutableInt64 { return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateCounter]) } diff --git a/contracts/wasm/testcore/src/state.rs b/contracts/wasm/testcore/src/state.rs index 1f62a862ac..ece8ea4db2 100644 --- a/contracts/wasm/testcore/src/state.rs +++ b/contracts/wasm/testcore/src/state.rs @@ -48,6 +48,10 @@ pub struct MutableTestCoreState { } impl MutableTestCoreState { + pub fn as_immutable(&self) -> ImmutableTestCoreState { + ImmutableTestCoreState { id: self.id } + } + pub fn counter(&self) -> ScMutableInt64 { ScMutableInt64::new(self.id, idx_map(IDX_STATE_COUNTER)) } diff --git a/contracts/wasm/testcore/ts/testcore/state.ts b/contracts/wasm/testcore/ts/testcore/state.ts index 3a20e3d512..7d6ff2c29e 100644 --- a/contracts/wasm/testcore/ts/testcore/state.ts +++ b/contracts/wasm/testcore/ts/testcore/state.ts @@ -32,6 +32,12 @@ export class ImmutableTestCoreState extends wasmlib.ScMapID { } export class MutableTestCoreState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableTestCoreState { + const imm = new sc.ImmutableTestCoreState(); + imm.mapID = this.mapID; + return imm; + } + counter(): wasmlib.ScMutableInt64 { return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateCounter]); } diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/state.go b/contracts/wasm/testwasmlib/go/testwasmlib/state.go index 7d08d8f873..31e2118eb1 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/state.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/state.go @@ -48,6 +48,10 @@ type MutableTestWasmLibState struct { id int32 } +func (s MutableTestWasmLibState) AsImmutable() ImmutableTestWasmLibState { + return ImmutableTestWasmLibState(s) +} + func (s MutableTestWasmLibState) Arrays() MapStringToMutableStringArray { mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateArrays], wasmlib.TYPE_MAP) return MapStringToMutableStringArray{objID: mapID} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go new file mode 100644 index 0000000000..e0c3faf93c --- /dev/null +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/events.go @@ -0,0 +1,28 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package testwasmlibclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +var EventHandlers = map[string]func([]string){ + "testwasmlib.test": onTestWasmLibTestThunk, +} + +type EventTest struct { + wasmclient.Event + Address wasmclient.Address + Name string +} + +func onTestWasmLibTestThunk(message []string) { + e := &EventTest{} + e.Init(message) + e.Address = e.NextAddress() + e.Name = e.NextString() + OnTestWasmLibTest(e) +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go new file mode 100644 index 0000000000..44566db07b --- /dev/null +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go @@ -0,0 +1,428 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package testwasmlibclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +const ( + ArgAddress = "address" + ArgAgentID = "agentID" + ArgBlockIndex = "blockIndex" + ArgBool = "bool" + ArgBytes = "bytes" + ArgChainID = "chainID" + ArgColor = "color" + ArgHash = "hash" + ArgHname = "hname" + ArgIndex = "index" + ArgInt16 = "int16" + ArgInt32 = "int32" + ArgInt64 = "int64" + ArgInt8 = "int8" + ArgName = "name" + ArgParam = "this" + ArgRecordIndex = "recordIndex" + ArgRequestID = "requestID" + ArgString = "string" + ArgUint16 = "uint16" + ArgUint32 = "uint32" + ArgUint64 = "uint64" + ArgUint8 = "uint8" + ArgValue = "value" + + ResCount = "count" + ResIotas = "iotas" + ResLength = "length" + ResRandom = "random" + ResRecord = "record" + ResValue = "value" +) + +///////////////////////////// arrayClear ///////////////////////////// + +type ArrayClearFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *ArrayClearFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f *ArrayClearFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgName) + return f.ClientFunc.Post(0x88021821, &f.args) +} + +///////////////////////////// arrayCreate ///////////////////////////// + +type ArrayCreateFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *ArrayCreateFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f *ArrayCreateFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgName) + return f.ClientFunc.Post(0x1ed5b23b, &f.args) +} + +///////////////////////////// arraySet ///////////////////////////// + +type ArraySetFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *ArraySetFunc) Index(v int32) { + f.args.SetInt32(ArgIndex, v) +} + +func (f *ArraySetFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f *ArraySetFunc) Value(v string) { + f.args.SetString(ArgValue, v) +} + +func (f *ArraySetFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgIndex) + f.args.Mandatory(ArgName) + f.args.Mandatory(ArgValue) + return f.ClientFunc.Post(0x2c4150b3, &f.args) +} + +///////////////////////////// paramTypes ///////////////////////////// + +type ParamTypesFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *ParamTypesFunc) Address(v wasmclient.Address) { + f.args.SetAddress(ArgAddress, v) +} + +func (f *ParamTypesFunc) AgentID(v wasmclient.AgentID) { + f.args.SetAgentID(ArgAgentID, v) +} + +func (f *ParamTypesFunc) Bool(v bool) { + f.args.SetBool(ArgBool, v) +} + +func (f *ParamTypesFunc) Bytes(v []byte) { + f.args.SetBytes(ArgBytes, v) +} + +func (f *ParamTypesFunc) ChainID(v wasmclient.ChainID) { + f.args.SetChainID(ArgChainID, v) +} + +func (f *ParamTypesFunc) Color(v wasmclient.Color) { + f.args.SetColor(ArgColor, v) +} + +func (f *ParamTypesFunc) Hash(v wasmclient.Hash) { + f.args.SetHash(ArgHash, v) +} + +func (f *ParamTypesFunc) Hname(v wasmclient.Hname) { + f.args.SetHname(ArgHname, v) +} + +func (f *ParamTypesFunc) Int16(v int16) { + f.args.SetInt16(ArgInt16, v) +} + +func (f *ParamTypesFunc) Int32(v int32) { + f.args.SetInt32(ArgInt32, v) +} + +func (f *ParamTypesFunc) Int64(v int64) { + f.args.SetInt64(ArgInt64, v) +} + +func (f *ParamTypesFunc) Int8(v int8) { + f.args.SetInt8(ArgInt8, v) +} + +func (f *ParamTypesFunc) Param(v []byte) { + f.args.SetBytes(ArgParam, v) +} + +func (f *ParamTypesFunc) RequestID(v wasmclient.RequestID) { + f.args.SetRequestID(ArgRequestID, v) +} + +func (f *ParamTypesFunc) String(v string) { + f.args.SetString(ArgString, v) +} + +func (f *ParamTypesFunc) Uint16(v uint16) { + f.args.SetUint16(ArgUint16, v) +} + +func (f *ParamTypesFunc) Uint32(v uint32) { + f.args.SetUint32(ArgUint32, v) +} + +func (f *ParamTypesFunc) Uint64(v uint64) { + f.args.SetUint64(ArgUint64, v) +} + +func (f *ParamTypesFunc) Uint8(v uint8) { + f.args.SetUint8(ArgUint8, v) +} + +func (f *ParamTypesFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0x6921c4cd, &f.args) +} + +///////////////////////////// random ///////////////////////////// + +type RandomFunc struct { + wasmclient.ClientFunc +} + +func (f *RandomFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0xe86c97ca, nil) +} + +///////////////////////////// triggerEvent ///////////////////////////// + +type TriggerEventFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *TriggerEventFunc) Address(v wasmclient.Address) { + f.args.SetAddress(ArgAddress, v) +} + +func (f *TriggerEventFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f *TriggerEventFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgAddress) + f.args.Mandatory(ArgName) + return f.ClientFunc.Post(0xd5438ac6, &f.args) +} + +///////////////////////////// arrayLength ///////////////////////////// + +type ArrayLengthView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *ArrayLengthView) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f *ArrayLengthView) Call() ArrayLengthResults { + f.args.Mandatory(ArgName) + f.ClientView.Call("arrayLength", &f.args) + return ArrayLengthResults{res: f.Results()} +} + +type ArrayLengthResults struct { + res wasmclient.Results +} + +func (r *ArrayLengthResults) Length() int32 { + return r.res.GetInt32(ResLength) +} + +///////////////////////////// arrayValue ///////////////////////////// + +type ArrayValueView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *ArrayValueView) Index(v int32) { + f.args.SetInt32(ArgIndex, v) +} + +func (f *ArrayValueView) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f *ArrayValueView) Call() ArrayValueResults { + f.args.Mandatory(ArgIndex) + f.args.Mandatory(ArgName) + f.ClientView.Call("arrayValue", &f.args) + return ArrayValueResults{res: f.Results()} +} + +type ArrayValueResults struct { + res wasmclient.Results +} + +func (r *ArrayValueResults) Value() string { + return r.res.GetString(ResValue) +} + +///////////////////////////// blockRecord ///////////////////////////// + +type BlockRecordView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *BlockRecordView) BlockIndex(v int32) { + f.args.SetInt32(ArgBlockIndex, v) +} + +func (f *BlockRecordView) RecordIndex(v int32) { + f.args.SetInt32(ArgRecordIndex, v) +} + +func (f *BlockRecordView) Call() BlockRecordResults { + f.args.Mandatory(ArgBlockIndex) + f.args.Mandatory(ArgRecordIndex) + f.ClientView.Call("blockRecord", &f.args) + return BlockRecordResults{res: f.Results()} +} + +type BlockRecordResults struct { + res wasmclient.Results +} + +func (r *BlockRecordResults) Record() []byte { + return r.res.GetBytes(ResRecord) +} + +///////////////////////////// blockRecords ///////////////////////////// + +type BlockRecordsView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *BlockRecordsView) BlockIndex(v int32) { + f.args.SetInt32(ArgBlockIndex, v) +} + +func (f *BlockRecordsView) Call() BlockRecordsResults { + f.args.Mandatory(ArgBlockIndex) + f.ClientView.Call("blockRecords", &f.args) + return BlockRecordsResults{res: f.Results()} +} + +type BlockRecordsResults struct { + res wasmclient.Results +} + +func (r *BlockRecordsResults) Count() int32 { + return r.res.GetInt32(ResCount) +} + +///////////////////////////// getRandom ///////////////////////////// + +type GetRandomView struct { + wasmclient.ClientView +} + +func (f *GetRandomView) Call() GetRandomResults { + f.ClientView.Call("getRandom", nil) + return GetRandomResults{res: f.Results()} +} + +type GetRandomResults struct { + res wasmclient.Results +} + +func (r *GetRandomResults) Random() int64 { + return r.res.GetInt64(ResRandom) +} + +///////////////////////////// iotaBalance ///////////////////////////// + +type IotaBalanceView struct { + wasmclient.ClientView +} + +func (f *IotaBalanceView) Call() IotaBalanceResults { + f.ClientView.Call("iotaBalance", nil) + return IotaBalanceResults{res: f.Results()} +} + +type IotaBalanceResults struct { + res wasmclient.Results +} + +func (r *IotaBalanceResults) Iotas() int64 { + return r.res.GetInt64(ResIotas) +} + +///////////////////////////// TestWasmLibService ///////////////////////////// + +type TestWasmLibService struct { + wasmclient.Service +} + +func NewTestWasmLibService(cl *wasmclient.ServiceClient, chainID string) (*TestWasmLibService, error) { + s := &TestWasmLibService{} + err := s.Service.Init(cl, chainID, 0x89703a45, EventHandlers) + return s, err +} + +func (s *TestWasmLibService) ArrayClear() ArrayClearFunc { + return ArrayClearFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *TestWasmLibService) ArrayCreate() ArrayCreateFunc { + return ArrayCreateFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *TestWasmLibService) ArraySet() ArraySetFunc { + return ArraySetFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *TestWasmLibService) ParamTypes() ParamTypesFunc { + return ParamTypesFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *TestWasmLibService) Random() RandomFunc { + return RandomFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *TestWasmLibService) TriggerEvent() TriggerEventFunc { + return TriggerEventFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *TestWasmLibService) ArrayLength() ArrayLengthView { + return ArrayLengthView{ClientView: s.AsClientView()} +} + +func (s *TestWasmLibService) ArrayValue() ArrayValueView { + return ArrayValueView{ClientView: s.AsClientView()} +} + +func (s *TestWasmLibService) BlockRecord() BlockRecordView { + return BlockRecordView{ClientView: s.AsClientView()} +} + +func (s *TestWasmLibService) BlockRecords() BlockRecordsView { + return BlockRecordsView{ClientView: s.AsClientView()} +} + +func (s *TestWasmLibService) GetRandom() GetRandomView { + return GetRandomView{ClientView: s.AsClientView()} +} + +func (s *TestWasmLibService) IotaBalance() IotaBalanceView { + return IotaBalanceView{ClientView: s.AsClientView()} +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go new file mode 100644 index 0000000000..205f7ffd4d --- /dev/null +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/testwasmlib.go @@ -0,0 +1,7 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package testwasmlibclient + +func OnTestWasmLibTest(event *EventTest) { +} diff --git a/contracts/wasm/testwasmlib/src/state.rs b/contracts/wasm/testwasmlib/src/state.rs index 541fceaf7f..80d6c0b01a 100644 --- a/contracts/wasm/testwasmlib/src/state.rs +++ b/contracts/wasm/testwasmlib/src/state.rs @@ -65,6 +65,10 @@ pub struct MutableTestWasmLibState { } impl MutableTestWasmLibState { + pub fn as_immutable(&self) -> ImmutableTestWasmLibState { + ImmutableTestWasmLibState { id: self.id } + } + pub fn arrays(&self) -> MapStringToMutableStringArray { let map_id = get_object_id(self.id, idx_map(IDX_STATE_ARRAYS), TYPE_MAP); MapStringToMutableStringArray { obj_id: map_id } diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go new file mode 100644 index 0000000000..0ca27b30c1 --- /dev/null +++ b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go @@ -0,0 +1,116 @@ +package test + +import ( + "fmt" + "testing" + + "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlibclient" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + "github.com/stretchr/testify/require" +) + +// hardcoded seed and chain ID, taken from wasp-cli.json +// note that normally the chain has already been set up and +// the contract has already been deployed in some way, so +// these values are usually available from elsewhere +const ( + mySeed = "6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv" + myChainID = "jn52vSuUUYY22T1mV2ny14EADYBu3ofyewLRSsVRnjpz" +) + +func setupClient(t *testing.T) *testwasmlibclient.TestWasmLibService { + require.True(t, wasmclient.SeedIsValid(mySeed)) + require.True(t, wasmclient.ChainIsValid(myChainID)) + + // we're testing against wasp-cluster, so defaults will do + svcClient := wasmclient.DefaultServiceClient() + + // create the service for the testwasmlib smart contract + svc, err := testwasmlibclient.NewTestWasmLibService(svcClient, myChainID) + require.NoError(t, err) + + // we'll use the first address in the seed to sign requests + svc.SignRequests(wasmclient.SeedToKeyPair(mySeed, 0)) + return svc +} + +func TestClientEvents(t *testing.T) { + svc := setupClient(t) + + // get new triggerEvent interface, pass params, and post the request + f := svc.TriggerEvent() + f.Name("Lala") + f.Address(wasmclient.SeedToAddress(mySeed, 0)) + req1 := f.Post() + require.NoError(t, req1.Error()) + + // err := svc.WaitRequest(req1) + // require.NoError(t, err) + + // get new triggerEvent interface, pass params, and post the request + f = svc.TriggerEvent() + f.Name("Trala") + f.Address(wasmclient.SeedToAddress(mySeed, 1)) + req2 := f.Post() + require.NoError(t, req2.Error()) + + err := svc.WaitRequest(req2) + require.NoError(t, err) +} + +func TestClientRandom(t *testing.T) { + svc := setupClient(t) + + // generate new random value + f := svc.Random() + req := f.Post() + require.NoError(t, req.Error()) + + err := svc.WaitRequest(req) + require.NoError(t, err) + + // get current random value + v := svc.GetRandom() + res := v.Call() + require.NoError(t, v.Error()) + require.GreaterOrEqual(t, res.Random(), int64(0)) + fmt.Println("Random: ", res.Random()) +} + +func TestClientArray(t *testing.T) { + svc := setupClient(t) + + v := svc.ArrayLength() + v.Name("Bands") + res := v.Call() + require.NoError(t, v.Error()) + require.EqualValues(t, 0, res.Length()) + + f := svc.ArraySet() + f.Name("Bands") + f.Index(0) + f.Value("Dire Straits") + req := f.Post() + require.NoError(t, req.Error()) + err := svc.WaitRequest(req) + require.NoError(t, err) + + v = svc.ArrayLength() + v.Name("Bands") + res = v.Call() + require.NoError(t, v.Error()) + require.EqualValues(t, 1, res.Length()) + + c := svc.ArrayClear() + c.Name("Bands") + req = c.Post() + require.NoError(t, req.Error()) + err = svc.WaitRequest(req) + require.NoError(t, err) + + v = svc.ArrayLength() + v.Name("Bands") + res = v.Call() + require.NoError(t, v.Error()) + require.EqualValues(t, 0, res.Length()) +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts index a823238696..f519d0bc00 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts @@ -50,6 +50,12 @@ export class MapStringToMutableStringArray { } export class MutableTestWasmLibState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableTestWasmLibState { + const imm = new sc.ImmutableTestWasmLibState(); + imm.mapID = this.mapID; + return imm; + } + arrays(): sc.MapStringToMutableStringArray { let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateArrays], wasmlib.TYPE_MAP); return new sc.MapStringToMutableStringArray(mapID); diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts new file mode 100644 index 0000000000..2e11655cef --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts @@ -0,0 +1,24 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmclient" +import * as app from "./testwasmlib" + +export const eventHandlers: wasmclient.EventHandlers = { + "testwasmlib.test": (msg: string[]) => app.onTestWasmLibTest(new EventTest(msg)), +}; + +export class EventTest extends wasmclient.Event { + public readonly address: wasmclient.Address; + public readonly name: wasmclient.String; + + public constructor(msg: string[]) { + super(msg) + this.address = this.nextAddress(); + this.name = this.nextString(); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts new file mode 100644 index 0000000000..f3eff69c33 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/index.ts @@ -0,0 +1,10 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +export * from "./testwasmlib"; +export * from "./events"; +export * from "./service"; diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts new file mode 100644 index 0000000000..1446631506 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -0,0 +1,398 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmclient" +import * as events from "./events" + +const ArgAddress = "address"; +const ArgAgentID = "agentID"; +const ArgBlockIndex = "blockIndex"; +const ArgBool = "bool"; +const ArgBytes = "bytes"; +const ArgChainID = "chainID"; +const ArgColor = "color"; +const ArgHash = "hash"; +const ArgHname = "hname"; +const ArgIndex = "index"; +const ArgInt16 = "int16"; +const ArgInt32 = "int32"; +const ArgInt64 = "int64"; +const ArgInt8 = "int8"; +const ArgName = "name"; +const ArgParam = "this"; +const ArgRecordIndex = "recordIndex"; +const ArgRequestID = "requestID"; +const ArgString = "string"; +const ArgUint16 = "uint16"; +const ArgUint32 = "uint32"; +const ArgUint64 = "uint64"; +const ArgUint8 = "uint8"; +const ArgValue = "value"; + +const ResCount = "count"; +const ResIotas = "iotas"; +const ResLength = "length"; +const ResRandom = "random"; +const ResRecord = "record"; +const ResValue = "value"; + +///////////////////////////// arrayClear ///////////////////////////// + +export class ArrayClearFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public name(v: string): void { + this.args.setString(ArgName, v); + } + + public async post(): Promise { + this.args.mandatory(ArgName); + return await super.post(0x88021821, this.args); + } +} + +///////////////////////////// arrayCreate ///////////////////////////// + +export class ArrayCreateFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public name(v: string): void { + this.args.setString(ArgName, v); + } + + public async post(): Promise { + this.args.mandatory(ArgName); + return await super.post(0x1ed5b23b, this.args); + } +} + +///////////////////////////// arraySet ///////////////////////////// + +export class ArraySetFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public index(v: wasmclient.Int32): void { + this.args.setInt32(ArgIndex, v); + } + + public name(v: string): void { + this.args.setString(ArgName, v); + } + + public value(v: string): void { + this.args.setString(ArgValue, v); + } + + public async post(): Promise { + this.args.mandatory(ArgIndex); + this.args.mandatory(ArgName); + this.args.mandatory(ArgValue); + return await super.post(0x2c4150b3, this.args); + } +} + +///////////////////////////// paramTypes ///////////////////////////// + +export class ParamTypesFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public address(v: wasmclient.Address): void { + this.args.setAddress(ArgAddress, v); + } + + public agentID(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgAgentID, v); + } + + public bool(v: boolean): void { + this.args.setBool(ArgBool, v); + } + + public bytes(v: wasmclient.Bytes): void { + this.args.setBytes(ArgBytes, v); + } + + public chainID(v: wasmclient.ChainID): void { + this.args.setChainID(ArgChainID, v); + } + + public color(v: wasmclient.Color): void { + this.args.setColor(ArgColor, v); + } + + public hash(v: wasmclient.Hash): void { + this.args.setHash(ArgHash, v); + } + + public hname(v: wasmclient.Hname): void { + this.args.setHname(ArgHname, v); + } + + public int16(v: wasmclient.Int16): void { + this.args.setInt16(ArgInt16, v); + } + + public int32(v: wasmclient.Int32): void { + this.args.setInt32(ArgInt32, v); + } + + public int64(v: wasmclient.Int64): void { + this.args.setInt64(ArgInt64, v); + } + + public int8(v: wasmclient.Int8): void { + this.args.setInt8(ArgInt8, v); + } + + public param(v: wasmclient.Bytes): void { + this.args.setBytes(ArgParam, v); + } + + public requestID(v: wasmclient.RequestID): void { + this.args.setRequestID(ArgRequestID, v); + } + + public string(v: string): void { + this.args.setString(ArgString, v); + } + + public uint16(v: wasmclient.Uint16): void { + this.args.setUint16(ArgUint16, v); + } + + public uint32(v: wasmclient.Uint32): void { + this.args.setUint32(ArgUint32, v); + } + + public uint64(v: wasmclient.Uint64): void { + this.args.setUint64(ArgUint64, v); + } + + public uint8(v: wasmclient.Uint8): void { + this.args.setUint8(ArgUint8, v); + } + + public async post(): Promise { + return await super.post(0x6921c4cd, this.args); + } +} + +///////////////////////////// random ///////////////////////////// + +export class RandomFunc extends wasmclient.ClientFunc { + + public async post(): Promise { + return await super.post(0xe86c97ca, null); + } +} + +///////////////////////////// triggerEvent ///////////////////////////// + +export class TriggerEventFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public address(v: wasmclient.Address): void { + this.args.setAddress(ArgAddress, v); + } + + public name(v: string): void { + this.args.setString(ArgName, v); + } + + public async post(): Promise { + this.args.mandatory(ArgAddress); + this.args.mandatory(ArgName); + return await super.post(0xd5438ac6, this.args); + } +} + +///////////////////////////// arrayLength ///////////////////////////// + +export class ArrayLengthView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public name(v: string): void { + this.args.setString(ArgName, v); + } + + public async call(): Promise { + this.args.mandatory(ArgName); + return new ArrayLengthResults(await this.callView("arrayLength", this.args)); + } +} + +export class ArrayLengthResults extends wasmclient.ViewResults { + + length(): wasmclient.Int32 { + return this.res.getInt32(ResLength); + } +} + +///////////////////////////// arrayValue ///////////////////////////// + +export class ArrayValueView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public index(v: wasmclient.Int32): void { + this.args.setInt32(ArgIndex, v); + } + + public name(v: string): void { + this.args.setString(ArgName, v); + } + + public async call(): Promise { + this.args.mandatory(ArgIndex); + this.args.mandatory(ArgName); + return new ArrayValueResults(await this.callView("arrayValue", this.args)); + } +} + +export class ArrayValueResults extends wasmclient.ViewResults { + + value(): string { + return this.res.getString(ResValue); + } +} + +///////////////////////////// blockRecord ///////////////////////////// + +export class BlockRecordView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public blockIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgBlockIndex, v); + } + + public recordIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgRecordIndex, v); + } + + public async call(): Promise { + this.args.mandatory(ArgBlockIndex); + this.args.mandatory(ArgRecordIndex); + return new BlockRecordResults(await this.callView("blockRecord", this.args)); + } +} + +export class BlockRecordResults extends wasmclient.ViewResults { + + record(): wasmclient.Bytes { + return this.res.getBytes(ResRecord); + } +} + +///////////////////////////// blockRecords ///////////////////////////// + +export class BlockRecordsView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public blockIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgBlockIndex, v); + } + + public async call(): Promise { + this.args.mandatory(ArgBlockIndex); + return new BlockRecordsResults(await this.callView("blockRecords", this.args)); + } +} + +export class BlockRecordsResults extends wasmclient.ViewResults { + + count(): wasmclient.Int32 { + return this.res.getInt32(ResCount); + } +} + +///////////////////////////// getRandom ///////////////////////////// + +export class GetRandomView extends wasmclient.ClientView { + + public async call(): Promise { + return new GetRandomResults(await this.callView("getRandom", null)); + } +} + +export class GetRandomResults extends wasmclient.ViewResults { + + random(): wasmclient.Int64 { + return this.res.getInt64(ResRandom); + } +} + +///////////////////////////// iotaBalance ///////////////////////////// + +export class IotaBalanceView extends wasmclient.ClientView { + + public async call(): Promise { + return new IotaBalanceResults(await this.callView("iotaBalance", null)); + } +} + +export class IotaBalanceResults extends wasmclient.ViewResults { + + iotas(): wasmclient.Int64 { + return this.res.getInt64(ResIotas); + } +} + +///////////////////////////// TestWasmLibService ///////////////////////////// + +export class TestWasmLibService extends wasmclient.Service { + + public constructor(cl: wasmclient.ServiceClient) { + super(cl, 0x89703a45, events.eventHandlers); + } + + public arrayClear(): ArrayClearFunc { + return new ArrayClearFunc(this); + } + + public arrayCreate(): ArrayCreateFunc { + return new ArrayCreateFunc(this); + } + + public arraySet(): ArraySetFunc { + return new ArraySetFunc(this); + } + + public paramTypes(): ParamTypesFunc { + return new ParamTypesFunc(this); + } + + public random(): RandomFunc { + return new RandomFunc(this); + } + + public triggerEvent(): TriggerEventFunc { + return new TriggerEventFunc(this); + } + + public arrayLength(): ArrayLengthView { + return new ArrayLengthView(this); + } + + public arrayValue(): ArrayValueView { + return new ArrayValueView(this); + } + + public blockRecord(): BlockRecordView { + return new BlockRecordView(this); + } + + public blockRecords(): BlockRecordsView { + return new BlockRecordsView(this); + } + + public getRandom(): GetRandomView { + return new GetRandomView(this); + } + + public iotaBalance(): IotaBalanceView { + return new IotaBalanceView(this); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts new file mode 100644 index 0000000000..cbae805e63 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/testwasmlib.ts @@ -0,0 +1,8 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as events from "./events" +import * as service from "./service" + +export function onTestWasmLibTest(event: events.EventTest): void { +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json b/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json new file mode 100644 index 0000000000..bc17186726 --- /dev/null +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": ["es2020"], + "target": "es2020", + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], +} diff --git a/contracts/wasm/timestamp/go/timestamp/state.go b/contracts/wasm/timestamp/go/timestamp/state.go index d57540232e..2e19f8d361 100644 --- a/contracts/wasm/timestamp/go/timestamp/state.go +++ b/contracts/wasm/timestamp/go/timestamp/state.go @@ -21,6 +21,10 @@ type MutabletimestampState struct { id int32 } +func (s MutabletimestampState) AsImmutable() ImmutabletimestampState { + return ImmutabletimestampState(s) +} + func (s MutabletimestampState) Timestamp() wasmlib.ScMutableInt64 { return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateTimestamp]) } diff --git a/contracts/wasm/timestamp/src/state.rs b/contracts/wasm/timestamp/src/state.rs index f5554bae10..bc097e03fa 100644 --- a/contracts/wasm/timestamp/src/state.rs +++ b/contracts/wasm/timestamp/src/state.rs @@ -31,6 +31,10 @@ pub struct MutabletimestampState { } impl MutabletimestampState { + pub fn as_immutable(&self) -> ImmutabletimestampState { + ImmutabletimestampState { id: self.id } + } + pub fn timestamp(&self) -> ScMutableInt64 { ScMutableInt64::new(self.id, idx_map(IDX_STATE_TIMESTAMP)) } diff --git a/contracts/wasm/timestamp/ts/timestamp/state.ts b/contracts/wasm/timestamp/ts/timestamp/state.ts index cfa1eaab07..47444c08dd 100644 --- a/contracts/wasm/timestamp/ts/timestamp/state.ts +++ b/contracts/wasm/timestamp/ts/timestamp/state.ts @@ -15,6 +15,12 @@ export class ImmutabletimestampState extends wasmlib.ScMapID { } export class MutabletimestampState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutabletimestampState { + const imm = new sc.ImmutabletimestampState(); + imm.mapID = this.mapID; + return imm; + } + timestamp(): wasmlib.ScMutableInt64 { return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateTimestamp]); } diff --git a/contracts/wasm/tokenregistry/go/tokenregistry/state.go b/contracts/wasm/tokenregistry/go/tokenregistry/state.go index 8b7ca0c52a..f8fbd2f4ea 100644 --- a/contracts/wasm/tokenregistry/go/tokenregistry/state.go +++ b/contracts/wasm/tokenregistry/go/tokenregistry/state.go @@ -75,6 +75,10 @@ type MutableTokenRegistryState struct { id int32 } +func (s MutableTokenRegistryState) AsImmutable() ImmutableTokenRegistryState { + return ImmutableTokenRegistryState(s) +} + func (s MutableTokenRegistryState) ColorList() ArrayOfMutableColor { arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateColorList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_COLOR) return ArrayOfMutableColor{objID: arrID} diff --git a/contracts/wasm/tokenregistry/src/state.rs b/contracts/wasm/tokenregistry/src/state.rs index 663b3d978a..9bf3a71326 100644 --- a/contracts/wasm/tokenregistry/src/state.rs +++ b/contracts/wasm/tokenregistry/src/state.rs @@ -98,6 +98,10 @@ pub struct MutableTokenRegistryState { } impl MutableTokenRegistryState { + pub fn as_immutable(&self) -> ImmutableTokenRegistryState { + ImmutableTokenRegistryState { id: self.id } + } + pub fn color_list(&self) -> ArrayOfMutableColor { let arr_id = get_object_id(self.id, idx_map(IDX_STATE_COLOR_LIST), TYPE_ARRAY | TYPE_COLOR); ArrayOfMutableColor { obj_id: arr_id } diff --git a/contracts/wasm/tokenregistry/ts/tokenregistry/state.ts b/contracts/wasm/tokenregistry/ts/tokenregistry/state.ts index bdbf15f919..14f73d6f32 100644 --- a/contracts/wasm/tokenregistry/ts/tokenregistry/state.ts +++ b/contracts/wasm/tokenregistry/ts/tokenregistry/state.ts @@ -85,6 +85,12 @@ export class MapColorToMutableToken { } export class MutableTokenRegistryState extends wasmlib.ScMapID { + asImmutable(): sc.ImmutableTokenRegistryState { + const imm = new sc.ImmutableTokenRegistryState(); + imm.mapID = this.mapID; + return imm; + } + colorList(): sc.ArrayOfMutableColor { let arrID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateColorList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_COLOR); return new sc.ArrayOfMutableColor(arrID); diff --git a/tools/schema/generator/gotemplates/state.go b/tools/schema/generator/gotemplates/state.go index 00dbd4163e..5cd4a0bd9a 100644 --- a/tools/schema/generator/gotemplates/state.go +++ b/tools/schema/generator/gotemplates/state.go @@ -19,6 +19,14 @@ $#each state proxyContainers type $TypeName struct { id int32 } +$#if mut stateProxyImmutableFunc $#each state proxyMethods +`, + // ******************************* + "stateProxyImmutableFunc": ` + +func (s $TypeName) AsImmutable() Immutable$Package$+State { + return Immutable$Package$+State(s) +} `, } diff --git a/tools/schema/generator/rstemplates/state.go b/tools/schema/generator/rstemplates/state.go index 2dbdabed25..b5aa40fd8a 100644 --- a/tools/schema/generator/rstemplates/state.go +++ b/tools/schema/generator/rstemplates/state.go @@ -35,7 +35,15 @@ $#if state stateProxyImpl impl $TypeName { $#set separator $false +$#if mut stateProxyImmutableFunc $#each state proxyMethods } +`, + // ******************************* + "stateProxyImmutableFunc": ` +$#set separator $true + pub fn as_immutable(&self) -> Immutable$Package$+State { + Immutable$Package$+State { id: self.id } + } `, } diff --git a/tools/schema/generator/tstemplates/state.go b/tools/schema/generator/tstemplates/state.go index f624be1471..979756a368 100644 --- a/tools/schema/generator/tstemplates/state.go +++ b/tools/schema/generator/tstemplates/state.go @@ -17,7 +17,17 @@ $#each state proxyContainers export class $TypeName extends wasmlib.ScMapID { $#set separator $false +$#if mut stateProxyImmutableFunc $#each state proxyMethods } +`, + // ******************************* + "stateProxyImmutableFunc": ` +$#set separator $true + asImmutable(): sc.Immutable$Package$+State { + const imm = new sc.Immutable$Package$+State(); + imm.mapID = this.mapID; + return imm; + } `, } From 42e2a597223b38c85e355382022d1afbb897d34e Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Wed, 5 Jan 2022 20:42:44 -0800 Subject: [PATCH 055/120] Temporary (suboptimal) fix for inability to call views between SCs --- contracts/wasm/dividend/go/dividend/params.go | 16 +- .../wasm/dividend/go/dividend/results.go | 8 +- contracts/wasm/dividend/go/dividend/state.go | 16 +- contracts/wasm/dividend/src/params.rs | 16 +- contracts/wasm/dividend/src/results.rs | 8 +- contracts/wasm/dividend/src/state.rs | 16 +- contracts/wasm/dividend/test/dividend_bg.wasm | Bin 32303 -> 32407 bytes contracts/wasm/dividend/ts/dividend/params.ts | 16 +- .../wasm/dividend/ts/dividend/results.ts | 8 +- contracts/wasm/dividend/ts/dividend/state.ts | 16 +- .../go/donatewithfeedback/params.go | 12 +- .../go/donatewithfeedback/results.go | 32 ++-- .../go/donatewithfeedback/state.go | 12 +- .../wasm/donatewithfeedback/src/params.rs | 12 +- .../wasm/donatewithfeedback/src/results.rs | 32 ++-- .../wasm/donatewithfeedback/src/state.rs | 12 +- .../test/donatewithfeedback_bg.wasm | Bin 36517 -> 36670 bytes .../ts/donatewithfeedback/params.ts | 12 +- .../ts/donatewithfeedback/results.ts | 32 ++-- .../ts/donatewithfeedback/state.ts | 12 +- contracts/wasm/erc20/go/erc20/params.go | 40 ++-- contracts/wasm/erc20/go/erc20/results.go | 12 +- contracts/wasm/erc20/go/erc20/state.go | 12 +- contracts/wasm/erc20/src/params.rs | 40 ++-- contracts/wasm/erc20/src/results.rs | 12 +- contracts/wasm/erc20/src/state.rs | 12 +- contracts/wasm/erc20/test/erc20_bg.wasm | Bin 35497 -> 35584 bytes contracts/wasm/erc20/ts/erc20/params.ts | 40 ++-- contracts/wasm/erc20/ts/erc20/results.ts | 12 +- contracts/wasm/erc20/ts/erc20/state.ts | 12 +- contracts/wasm/erc721/go/erc721/params.go | 76 ++++---- contracts/wasm/erc721/go/erc721/results.go | 28 +-- contracts/wasm/erc721/go/erc721/state.go | 24 +-- contracts/wasm/erc721/src/params.rs | 76 ++++---- contracts/wasm/erc721/src/results.rs | 28 +-- contracts/wasm/erc721/src/state.rs | 24 +-- contracts/wasm/erc721/test/erc721_bg.wasm | Bin 39496 -> 39777 bytes contracts/wasm/erc721/ts/erc721/params.ts | 76 ++++---- contracts/wasm/erc721/ts/erc721/results.ts | 28 +-- contracts/wasm/erc721/ts/erc721/state.ts | 24 +-- .../wasm/fairauction/go/fairauction/params.go | 32 ++-- .../fairauction/go/fairauction/results.go | 48 ++--- .../wasm/fairauction/go/fairauction/state.go | 16 +- contracts/wasm/fairauction/src/params.rs | 32 ++-- contracts/wasm/fairauction/src/results.rs | 48 ++--- contracts/wasm/fairauction/src/state.rs | 16 +- .../wasm/fairauction/test/fairauction_bg.wasm | Bin 42692 -> 42971 bytes .../wasm/fairauction/ts/fairauction/params.ts | 32 ++-- .../fairauction/ts/fairauction/results.ts | 48 ++--- .../wasm/fairauction/ts/fairauction/state.ts | 16 +- .../fairroulette/go/fairroulette/params.go | 8 +- .../fairroulette/go/fairroulette/results.go | 16 +- .../fairroulette/go/fairroulette/state.go | 24 +-- contracts/wasm/fairroulette/src/params.rs | 8 +- contracts/wasm/fairroulette/src/results.rs | 16 +- contracts/wasm/fairroulette/src/state.rs | 24 +-- .../fairroulette/test/fairroulette_bg.wasm | Bin 40121 -> 40322 bytes .../fairroulette/ts/fairroulette/params.ts | 8 +- .../fairroulette/ts/fairroulette/results.ts | 16 +- .../fairroulette/ts/fairroulette/state.ts | 24 +-- .../wasm/helloworld/go/helloworld/results.go | 4 +- contracts/wasm/helloworld/src/results.rs | 4 +- .../wasm/helloworld/test/helloworld_bg.wasm | Bin 15362 -> 15381 bytes .../wasm/helloworld/ts/helloworld/results.ts | 4 +- .../wasm/inccounter/go/inccounter/params.go | 12 +- .../wasm/inccounter/go/inccounter/results.go | 4 +- .../wasm/inccounter/go/inccounter/state.go | 8 +- contracts/wasm/inccounter/src/params.rs | 12 +- contracts/wasm/inccounter/src/results.rs | 4 +- contracts/wasm/inccounter/src/state.rs | 8 +- .../wasm/inccounter/test/inccounter_bg.wasm | Bin 37007 -> 37085 bytes .../wasm/inccounter/ts/inccounter/params.ts | 12 +- .../wasm/inccounter/ts/inccounter/results.ts | 4 +- .../wasm/inccounter/ts/inccounter/state.ts | 8 +- contracts/wasm/testcore/go/testcore/params.go | 176 +++++++++--------- .../wasm/testcore/go/testcore/results.go | 36 ++-- contracts/wasm/testcore/go/testcore/state.go | 20 +- contracts/wasm/testcore/src/params.rs | 176 +++++++++--------- contracts/wasm/testcore/src/results.rs | 36 ++-- contracts/wasm/testcore/src/state.rs | 20 +- contracts/wasm/testcore/test/testcore_bg.wasm | Bin 53153 -> 53613 bytes contracts/wasm/testcore/ts/testcore/params.ts | 176 +++++++++--------- .../wasm/testcore/ts/testcore/results.ts | 36 ++-- contracts/wasm/testcore/ts/testcore/state.ts | 20 +- .../wasm/testwasmlib/go/testwasmlib/params.go | 124 ++++++------ .../testwasmlib/go/testwasmlib/results.go | 24 +-- .../wasm/testwasmlib/go/testwasmlib/state.go | 8 +- contracts/wasm/testwasmlib/src/params.rs | 124 ++++++------ contracts/wasm/testwasmlib/src/results.rs | 24 +-- contracts/wasm/testwasmlib/src/state.rs | 8 +- .../wasm/testwasmlib/test/testwasmlib_bg.wasm | Bin 43545 -> 43872 bytes .../test/testwasmlib_client_test.go | 3 + .../wasm/testwasmlib/ts/testwasmlib/params.ts | 124 ++++++------ .../testwasmlib/ts/testwasmlib/results.ts | 24 +-- .../wasm/testwasmlib/ts/testwasmlib/state.ts | 8 +- .../wasm/timestamp/go/timestamp/results.go | 4 +- .../wasm/timestamp/go/timestamp/state.go | 4 +- contracts/wasm/timestamp/src/results.rs | 4 +- contracts/wasm/timestamp/src/state.rs | 4 +- .../wasm/timestamp/test/timestamp_bg.wasm | Bin 28155 -> 28192 bytes .../wasm/timestamp/ts/timestamp/results.ts | 4 +- .../wasm/timestamp/ts/timestamp/state.ts | 4 +- .../tokenregistry/go/tokenregistry/params.go | 20 +- .../tokenregistry/go/tokenregistry/state.go | 8 +- contracts/wasm/tokenregistry/src/params.rs | 20 +- contracts/wasm/tokenregistry/src/state.rs | 8 +- .../tokenregistry/test/tokenregistry_bg.wasm | Bin 32014 -> 32083 bytes .../tokenregistry/ts/tokenregistry/params.ts | 20 +- .../tokenregistry/ts/tokenregistry/state.ts | 8 +- .../sbtests/sbtestsc/testcore_bg.wasm | Bin 53153 -> 53613 bytes packages/vm/wasmsolo/solocontext.go | 4 +- tools/cluster/tests/wasm/inccounter_bg.wasm | Bin 37007 -> 37085 bytes tools/schema/generator/gotemplates/proxy.go | 7 +- tools/schema/generator/rstemplates/proxy.go | 7 +- tools/schema/generator/tstemplates/proxy.go | 7 +- 115 files changed, 1349 insertions(+), 1331 deletions(-) diff --git a/contracts/wasm/dividend/go/dividend/params.go b/contracts/wasm/dividend/go/dividend/params.go index 4306dec638..55d5460569 100644 --- a/contracts/wasm/dividend/go/dividend/params.go +++ b/contracts/wasm/dividend/go/dividend/params.go @@ -30,11 +30,11 @@ type ImmutableMemberParams struct { } func (s ImmutableMemberParams) Address() wasmlib.ScImmutableAddress { - return wasmlib.NewScImmutableAddress(s.id, idxMap[IdxParamAddress]) + return wasmlib.NewScImmutableAddress(s.id, wasmlib.KeyID(ParamAddress)) } func (s ImmutableMemberParams) Factor() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxParamFactor]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ParamFactor)) } type MutableMemberParams struct { @@ -42,11 +42,11 @@ type MutableMemberParams struct { } func (s MutableMemberParams) Address() wasmlib.ScMutableAddress { - return wasmlib.NewScMutableAddress(s.id, idxMap[IdxParamAddress]) + return wasmlib.NewScMutableAddress(s.id, wasmlib.KeyID(ParamAddress)) } func (s MutableMemberParams) Factor() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxParamFactor]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ParamFactor)) } type ImmutableSetOwnerParams struct { @@ -54,7 +54,7 @@ type ImmutableSetOwnerParams struct { } func (s ImmutableSetOwnerParams) Owner() wasmlib.ScImmutableAgentID { - return wasmlib.NewScImmutableAgentID(s.id, idxMap[IdxParamOwner]) + return wasmlib.NewScImmutableAgentID(s.id, wasmlib.KeyID(ParamOwner)) } type MutableSetOwnerParams struct { @@ -62,7 +62,7 @@ type MutableSetOwnerParams struct { } func (s MutableSetOwnerParams) Owner() wasmlib.ScMutableAgentID { - return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxParamOwner]) + return wasmlib.NewScMutableAgentID(s.id, wasmlib.KeyID(ParamOwner)) } type ImmutableGetFactorParams struct { @@ -70,7 +70,7 @@ type ImmutableGetFactorParams struct { } func (s ImmutableGetFactorParams) Address() wasmlib.ScImmutableAddress { - return wasmlib.NewScImmutableAddress(s.id, idxMap[IdxParamAddress]) + return wasmlib.NewScImmutableAddress(s.id, wasmlib.KeyID(ParamAddress)) } type MutableGetFactorParams struct { @@ -78,5 +78,5 @@ type MutableGetFactorParams struct { } func (s MutableGetFactorParams) Address() wasmlib.ScMutableAddress { - return wasmlib.NewScMutableAddress(s.id, idxMap[IdxParamAddress]) + return wasmlib.NewScMutableAddress(s.id, wasmlib.KeyID(ParamAddress)) } diff --git a/contracts/wasm/dividend/go/dividend/results.go b/contracts/wasm/dividend/go/dividend/results.go index 1e4f12e3ed..987b0fec57 100644 --- a/contracts/wasm/dividend/go/dividend/results.go +++ b/contracts/wasm/dividend/go/dividend/results.go @@ -14,7 +14,7 @@ type ImmutableGetFactorResults struct { } func (s ImmutableGetFactorResults) Factor() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultFactor]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultFactor)) } type MutableGetFactorResults struct { @@ -22,7 +22,7 @@ type MutableGetFactorResults struct { } func (s MutableGetFactorResults) Factor() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultFactor]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultFactor)) } type ImmutableGetOwnerResults struct { @@ -30,7 +30,7 @@ type ImmutableGetOwnerResults struct { } func (s ImmutableGetOwnerResults) Owner() wasmlib.ScImmutableAgentID { - return wasmlib.NewScImmutableAgentID(s.id, idxMap[IdxResultOwner]) + return wasmlib.NewScImmutableAgentID(s.id, wasmlib.KeyID(ResultOwner)) } type MutableGetOwnerResults struct { @@ -38,5 +38,5 @@ type MutableGetOwnerResults struct { } func (s MutableGetOwnerResults) Owner() wasmlib.ScMutableAgentID { - return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxResultOwner]) + return wasmlib.NewScMutableAgentID(s.id, wasmlib.KeyID(ResultOwner)) } diff --git a/contracts/wasm/dividend/go/dividend/state.go b/contracts/wasm/dividend/go/dividend/state.go index 3bf445ac89..b63408d3a3 100644 --- a/contracts/wasm/dividend/go/dividend/state.go +++ b/contracts/wasm/dividend/go/dividend/state.go @@ -34,21 +34,21 @@ type ImmutableDividendState struct { } func (s ImmutableDividendState) MemberList() ArrayOfImmutableAddress { - arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS) + arrID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateMemberList), wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS) return ArrayOfImmutableAddress{objID: arrID} } func (s ImmutableDividendState) Members() MapAddressToImmutableInt64 { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMembers], wasmlib.TYPE_MAP) + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateMembers), wasmlib.TYPE_MAP) return MapAddressToImmutableInt64{objID: mapID} } func (s ImmutableDividendState) Owner() wasmlib.ScImmutableAgentID { - return wasmlib.NewScImmutableAgentID(s.id, idxMap[IdxStateOwner]) + return wasmlib.NewScImmutableAgentID(s.id, wasmlib.KeyID(StateOwner)) } func (s ImmutableDividendState) TotalFactor() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxStateTotalFactor]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(StateTotalFactor)) } type ArrayOfMutableAddress struct { @@ -88,19 +88,19 @@ func (s MutableDividendState) AsImmutable() ImmutableDividendState { } func (s MutableDividendState) MemberList() ArrayOfMutableAddress { - arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMemberList], wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS) + arrID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateMemberList), wasmlib.TYPE_ARRAY|wasmlib.TYPE_ADDRESS) return ArrayOfMutableAddress{objID: arrID} } func (s MutableDividendState) Members() MapAddressToMutableInt64 { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateMembers], wasmlib.TYPE_MAP) + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateMembers), wasmlib.TYPE_MAP) return MapAddressToMutableInt64{objID: mapID} } func (s MutableDividendState) Owner() wasmlib.ScMutableAgentID { - return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxStateOwner]) + return wasmlib.NewScMutableAgentID(s.id, wasmlib.KeyID(StateOwner)) } func (s MutableDividendState) TotalFactor() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateTotalFactor]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(StateTotalFactor)) } diff --git a/contracts/wasm/dividend/src/params.rs b/contracts/wasm/dividend/src/params.rs index a8ddbef09f..abea99eece 100644 --- a/contracts/wasm/dividend/src/params.rs +++ b/contracts/wasm/dividend/src/params.rs @@ -43,11 +43,11 @@ pub struct ImmutableMemberParams { impl ImmutableMemberParams { pub fn address(&self) -> ScImmutableAddress { - ScImmutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScImmutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn factor(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_FACTOR)) + ScImmutableInt64::new(self.id, PARAM_FACTOR.get_key_id()) } } @@ -58,11 +58,11 @@ pub struct MutableMemberParams { impl MutableMemberParams { pub fn address(&self) -> ScMutableAddress { - ScMutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScMutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn factor(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_FACTOR)) + ScMutableInt64::new(self.id, PARAM_FACTOR.get_key_id()) } } @@ -73,7 +73,7 @@ pub struct ImmutableSetOwnerParams { impl ImmutableSetOwnerParams { pub fn owner(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_OWNER)) + ScImmutableAgentID::new(self.id, PARAM_OWNER.get_key_id()) } } @@ -84,7 +84,7 @@ pub struct MutableSetOwnerParams { impl MutableSetOwnerParams { pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_OWNER)) + ScMutableAgentID::new(self.id, PARAM_OWNER.get_key_id()) } } @@ -95,7 +95,7 @@ pub struct ImmutableGetFactorParams { impl ImmutableGetFactorParams { pub fn address(&self) -> ScImmutableAddress { - ScImmutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScImmutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } } @@ -106,6 +106,6 @@ pub struct MutableGetFactorParams { impl MutableGetFactorParams { pub fn address(&self) -> ScMutableAddress { - ScMutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScMutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } } diff --git a/contracts/wasm/dividend/src/results.rs b/contracts/wasm/dividend/src/results.rs index 373e293a53..9e6680e864 100644 --- a/contracts/wasm/dividend/src/results.rs +++ b/contracts/wasm/dividend/src/results.rs @@ -21,7 +21,7 @@ pub struct ImmutableGetFactorResults { impl ImmutableGetFactorResults { pub fn factor(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_FACTOR)) + ScImmutableInt64::new(self.id, RESULT_FACTOR.get_key_id()) } } @@ -32,7 +32,7 @@ pub struct MutableGetFactorResults { impl MutableGetFactorResults { pub fn factor(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_FACTOR)) + ScMutableInt64::new(self.id, RESULT_FACTOR.get_key_id()) } } @@ -43,7 +43,7 @@ pub struct ImmutableGetOwnerResults { impl ImmutableGetOwnerResults { pub fn owner(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_RESULT_OWNER)) + ScImmutableAgentID::new(self.id, RESULT_OWNER.get_key_id()) } } @@ -54,6 +54,6 @@ pub struct MutableGetOwnerResults { impl MutableGetOwnerResults { pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_RESULT_OWNER)) + ScMutableAgentID::new(self.id, RESULT_OWNER.get_key_id()) } } diff --git a/contracts/wasm/dividend/src/state.rs b/contracts/wasm/dividend/src/state.rs index 70180a46d7..4cb5424635 100644 --- a/contracts/wasm/dividend/src/state.rs +++ b/contracts/wasm/dividend/src/state.rs @@ -47,21 +47,21 @@ pub struct ImmutableDividendState { impl ImmutableDividendState { pub fn member_list(&self) -> ArrayOfImmutableAddress { - let arr_id = get_object_id(self.id, idx_map(IDX_STATE_MEMBER_LIST), TYPE_ARRAY | TYPE_ADDRESS); + let arr_id = get_object_id(self.id, STATE_MEMBER_LIST.get_key_id(), TYPE_ARRAY | TYPE_ADDRESS); ArrayOfImmutableAddress { obj_id: arr_id } } pub fn members(&self) -> MapAddressToImmutableInt64 { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_MEMBERS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_MEMBERS.get_key_id(), TYPE_MAP); MapAddressToImmutableInt64 { obj_id: map_id } } pub fn owner(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_STATE_OWNER)) + ScImmutableAgentID::new(self.id, STATE_OWNER.get_key_id()) } pub fn total_factor(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_TOTAL_FACTOR)) + ScImmutableInt64::new(self.id, STATE_TOTAL_FACTOR.get_key_id()) } } @@ -110,20 +110,20 @@ impl MutableDividendState { } pub fn member_list(&self) -> ArrayOfMutableAddress { - let arr_id = get_object_id(self.id, idx_map(IDX_STATE_MEMBER_LIST), TYPE_ARRAY | TYPE_ADDRESS); + let arr_id = get_object_id(self.id, STATE_MEMBER_LIST.get_key_id(), TYPE_ARRAY | TYPE_ADDRESS); ArrayOfMutableAddress { obj_id: arr_id } } pub fn members(&self) -> MapAddressToMutableInt64 { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_MEMBERS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_MEMBERS.get_key_id(), TYPE_MAP); MapAddressToMutableInt64 { obj_id: map_id } } pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_STATE_OWNER)) + ScMutableAgentID::new(self.id, STATE_OWNER.get_key_id()) } pub fn total_factor(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_TOTAL_FACTOR)) + ScMutableInt64::new(self.id, STATE_TOTAL_FACTOR.get_key_id()) } } diff --git a/contracts/wasm/dividend/test/dividend_bg.wasm b/contracts/wasm/dividend/test/dividend_bg.wasm index b80b9d3f3840af2c9692fbbbe032b9d52fa8ec15..d7848e5e3c9529d4cf4f3111d38d1f64d5d705ab 100644 GIT binary patch delta 6350 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 delta 6216 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 ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_FEEDBACK)) + ScImmutableString::new(self.id, PARAM_FEEDBACK.get_key_id()) } } @@ -33,7 +33,7 @@ pub struct MutableDonateParams { impl MutableDonateParams { pub fn feedback(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_FEEDBACK)) + ScMutableString::new(self.id, PARAM_FEEDBACK.get_key_id()) } } @@ -44,7 +44,7 @@ pub struct ImmutableWithdrawParams { impl ImmutableWithdrawParams { pub fn amount(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_AMOUNT)) + ScImmutableInt64::new(self.id, PARAM_AMOUNT.get_key_id()) } } @@ -55,7 +55,7 @@ pub struct MutableWithdrawParams { impl MutableWithdrawParams { pub fn amount(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_AMOUNT)) + ScMutableInt64::new(self.id, PARAM_AMOUNT.get_key_id()) } } @@ -66,7 +66,7 @@ pub struct ImmutableDonationParams { impl ImmutableDonationParams { pub fn nr(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_NR)) + ScImmutableInt64::new(self.id, PARAM_NR.get_key_id()) } } @@ -77,6 +77,6 @@ pub struct MutableDonationParams { impl MutableDonationParams { pub fn nr(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_NR)) + ScMutableInt64::new(self.id, PARAM_NR.get_key_id()) } } diff --git a/contracts/wasm/donatewithfeedback/src/results.rs b/contracts/wasm/donatewithfeedback/src/results.rs index 6d3b0ad322..22e641f04f 100644 --- a/contracts/wasm/donatewithfeedback/src/results.rs +++ b/contracts/wasm/donatewithfeedback/src/results.rs @@ -22,23 +22,23 @@ pub struct ImmutableDonationResults { impl ImmutableDonationResults { pub fn amount(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_AMOUNT)) + ScImmutableInt64::new(self.id, RESULT_AMOUNT.get_key_id()) } pub fn donator(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_RESULT_DONATOR)) + ScImmutableAgentID::new(self.id, RESULT_DONATOR.get_key_id()) } pub fn error(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_ERROR)) + ScImmutableString::new(self.id, RESULT_ERROR.get_key_id()) } pub fn feedback(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_FEEDBACK)) + ScImmutableString::new(self.id, RESULT_FEEDBACK.get_key_id()) } pub fn timestamp(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_TIMESTAMP)) + ScImmutableInt64::new(self.id, RESULT_TIMESTAMP.get_key_id()) } } @@ -49,23 +49,23 @@ pub struct MutableDonationResults { impl MutableDonationResults { pub fn amount(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_AMOUNT)) + ScMutableInt64::new(self.id, RESULT_AMOUNT.get_key_id()) } pub fn donator(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_RESULT_DONATOR)) + ScMutableAgentID::new(self.id, RESULT_DONATOR.get_key_id()) } pub fn error(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_ERROR)) + ScMutableString::new(self.id, RESULT_ERROR.get_key_id()) } pub fn feedback(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_FEEDBACK)) + ScMutableString::new(self.id, RESULT_FEEDBACK.get_key_id()) } pub fn timestamp(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_TIMESTAMP)) + ScMutableInt64::new(self.id, RESULT_TIMESTAMP.get_key_id()) } } @@ -76,15 +76,15 @@ pub struct ImmutableDonationInfoResults { impl ImmutableDonationInfoResults { pub fn count(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_COUNT)) + ScImmutableInt64::new(self.id, RESULT_COUNT.get_key_id()) } pub fn max_donation(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_MAX_DONATION)) + ScImmutableInt64::new(self.id, RESULT_MAX_DONATION.get_key_id()) } pub fn total_donation(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_TOTAL_DONATION)) + ScImmutableInt64::new(self.id, RESULT_TOTAL_DONATION.get_key_id()) } } @@ -95,14 +95,14 @@ pub struct MutableDonationInfoResults { impl MutableDonationInfoResults { pub fn count(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_COUNT)) + ScMutableInt64::new(self.id, RESULT_COUNT.get_key_id()) } pub fn max_donation(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_MAX_DONATION)) + ScMutableInt64::new(self.id, RESULT_MAX_DONATION.get_key_id()) } pub fn total_donation(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_TOTAL_DONATION)) + ScMutableInt64::new(self.id, RESULT_TOTAL_DONATION.get_key_id()) } } diff --git a/contracts/wasm/donatewithfeedback/src/state.rs b/contracts/wasm/donatewithfeedback/src/state.rs index 8834e2bf04..5f8dc08729 100644 --- a/contracts/wasm/donatewithfeedback/src/state.rs +++ b/contracts/wasm/donatewithfeedback/src/state.rs @@ -37,16 +37,16 @@ pub struct ImmutableDonateWithFeedbackState { impl ImmutableDonateWithFeedbackState { pub fn log(&self) -> ArrayOfImmutableDonation { - let arr_id = get_object_id(self.id, idx_map(IDX_STATE_LOG), TYPE_ARRAY | TYPE_BYTES); + let arr_id = get_object_id(self.id, STATE_LOG.get_key_id(), TYPE_ARRAY | TYPE_BYTES); ArrayOfImmutableDonation { obj_id: arr_id } } pub fn max_donation(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_MAX_DONATION)) + ScImmutableInt64::new(self.id, STATE_MAX_DONATION.get_key_id()) } pub fn total_donation(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_TOTAL_DONATION)) + ScImmutableInt64::new(self.id, STATE_TOTAL_DONATION.get_key_id()) } } @@ -80,15 +80,15 @@ impl MutableDonateWithFeedbackState { } pub fn log(&self) -> ArrayOfMutableDonation { - let arr_id = get_object_id(self.id, idx_map(IDX_STATE_LOG), TYPE_ARRAY | TYPE_BYTES); + let arr_id = get_object_id(self.id, STATE_LOG.get_key_id(), TYPE_ARRAY | TYPE_BYTES); ArrayOfMutableDonation { obj_id: arr_id } } pub fn max_donation(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_MAX_DONATION)) + ScMutableInt64::new(self.id, STATE_MAX_DONATION.get_key_id()) } pub fn total_donation(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_TOTAL_DONATION)) + ScMutableInt64::new(self.id, STATE_TOTAL_DONATION.get_key_id()) } } diff --git a/contracts/wasm/donatewithfeedback/test/donatewithfeedback_bg.wasm b/contracts/wasm/donatewithfeedback/test/donatewithfeedback_bg.wasm index 7f50ea8a95dc00eba97317c0c5397f81f38a273c..742ef0a8bc768622cfeba0f0c8b1790aafb227fb 100644 GIT binary patch delta 7048 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 delta 6790 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 ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_AMOUNT)) + ScImmutableInt64::new(self.id, PARAM_AMOUNT.get_key_id()) } pub fn delegation(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_DELEGATION)) + ScImmutableAgentID::new(self.id, PARAM_DELEGATION.get_key_id()) } } @@ -37,11 +37,11 @@ pub struct MutableApproveParams { impl MutableApproveParams { pub fn amount(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_AMOUNT)) + ScMutableInt64::new(self.id, PARAM_AMOUNT.get_key_id()) } pub fn delegation(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_DELEGATION)) + ScMutableAgentID::new(self.id, PARAM_DELEGATION.get_key_id()) } } @@ -82,11 +82,11 @@ pub struct ImmutableTransferParams { impl ImmutableTransferParams { pub fn account(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_ACCOUNT)) + ScImmutableAgentID::new(self.id, PARAM_ACCOUNT.get_key_id()) } pub fn amount(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_AMOUNT)) + ScImmutableInt64::new(self.id, PARAM_AMOUNT.get_key_id()) } } @@ -97,11 +97,11 @@ pub struct MutableTransferParams { impl MutableTransferParams { pub fn account(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_ACCOUNT)) + ScMutableAgentID::new(self.id, PARAM_ACCOUNT.get_key_id()) } pub fn amount(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_AMOUNT)) + ScMutableInt64::new(self.id, PARAM_AMOUNT.get_key_id()) } } @@ -112,15 +112,15 @@ pub struct ImmutableTransferFromParams { impl ImmutableTransferFromParams { pub fn account(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_ACCOUNT)) + ScImmutableAgentID::new(self.id, PARAM_ACCOUNT.get_key_id()) } pub fn amount(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_AMOUNT)) + ScImmutableInt64::new(self.id, PARAM_AMOUNT.get_key_id()) } pub fn recipient(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_RECIPIENT)) + ScImmutableAgentID::new(self.id, PARAM_RECIPIENT.get_key_id()) } } @@ -131,15 +131,15 @@ pub struct MutableTransferFromParams { impl MutableTransferFromParams { pub fn account(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_ACCOUNT)) + ScMutableAgentID::new(self.id, PARAM_ACCOUNT.get_key_id()) } pub fn amount(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_AMOUNT)) + ScMutableInt64::new(self.id, PARAM_AMOUNT.get_key_id()) } pub fn recipient(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_RECIPIENT)) + ScMutableAgentID::new(self.id, PARAM_RECIPIENT.get_key_id()) } } @@ -150,11 +150,11 @@ pub struct ImmutableAllowanceParams { impl ImmutableAllowanceParams { pub fn account(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_ACCOUNT)) + ScImmutableAgentID::new(self.id, PARAM_ACCOUNT.get_key_id()) } pub fn delegation(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_DELEGATION)) + ScImmutableAgentID::new(self.id, PARAM_DELEGATION.get_key_id()) } } @@ -165,11 +165,11 @@ pub struct MutableAllowanceParams { impl MutableAllowanceParams { pub fn account(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_ACCOUNT)) + ScMutableAgentID::new(self.id, PARAM_ACCOUNT.get_key_id()) } pub fn delegation(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_DELEGATION)) + ScMutableAgentID::new(self.id, PARAM_DELEGATION.get_key_id()) } } @@ -180,7 +180,7 @@ pub struct ImmutableBalanceOfParams { impl ImmutableBalanceOfParams { pub fn account(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_ACCOUNT)) + ScImmutableAgentID::new(self.id, PARAM_ACCOUNT.get_key_id()) } } @@ -191,6 +191,6 @@ pub struct MutableBalanceOfParams { impl MutableBalanceOfParams { pub fn account(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_ACCOUNT)) + ScMutableAgentID::new(self.id, PARAM_ACCOUNT.get_key_id()) } } diff --git a/contracts/wasm/erc20/src/results.rs b/contracts/wasm/erc20/src/results.rs index 8d01ae636d..dd77540b94 100644 --- a/contracts/wasm/erc20/src/results.rs +++ b/contracts/wasm/erc20/src/results.rs @@ -22,7 +22,7 @@ pub struct ImmutableAllowanceResults { impl ImmutableAllowanceResults { pub fn amount(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_AMOUNT)) + ScImmutableInt64::new(self.id, RESULT_AMOUNT.get_key_id()) } } @@ -33,7 +33,7 @@ pub struct MutableAllowanceResults { impl MutableAllowanceResults { pub fn amount(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_AMOUNT)) + ScMutableInt64::new(self.id, RESULT_AMOUNT.get_key_id()) } } @@ -44,7 +44,7 @@ pub struct ImmutableBalanceOfResults { impl ImmutableBalanceOfResults { pub fn amount(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_AMOUNT)) + ScImmutableInt64::new(self.id, RESULT_AMOUNT.get_key_id()) } } @@ -55,7 +55,7 @@ pub struct MutableBalanceOfResults { impl MutableBalanceOfResults { pub fn amount(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_AMOUNT)) + ScMutableInt64::new(self.id, RESULT_AMOUNT.get_key_id()) } } @@ -66,7 +66,7 @@ pub struct ImmutableTotalSupplyResults { impl ImmutableTotalSupplyResults { pub fn supply(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_SUPPLY)) + ScImmutableInt64::new(self.id, RESULT_SUPPLY.get_key_id()) } } @@ -77,6 +77,6 @@ pub struct MutableTotalSupplyResults { impl MutableTotalSupplyResults { pub fn supply(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_SUPPLY)) + ScMutableInt64::new(self.id, RESULT_SUPPLY.get_key_id()) } } diff --git a/contracts/wasm/erc20/src/state.rs b/contracts/wasm/erc20/src/state.rs index 6cd4cf35e8..e1e0ff3283 100644 --- a/contracts/wasm/erc20/src/state.rs +++ b/contracts/wasm/erc20/src/state.rs @@ -34,17 +34,17 @@ pub struct ImmutableErc20State { impl ImmutableErc20State { pub fn all_allowances(&self) -> MapAgentIDToImmutableAllowancesForAgent { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_ALL_ALLOWANCES), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_ALL_ALLOWANCES.get_key_id(), TYPE_MAP); MapAgentIDToImmutableAllowancesForAgent { obj_id: map_id } } pub fn balances(&self) -> MapAgentIDToImmutableInt64 { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_BALANCES), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_BALANCES.get_key_id(), TYPE_MAP); MapAgentIDToImmutableInt64 { obj_id: map_id } } pub fn supply(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_SUPPLY)) + ScImmutableInt64::new(self.id, STATE_SUPPLY.get_key_id()) } } @@ -75,16 +75,16 @@ impl MutableErc20State { } pub fn all_allowances(&self) -> MapAgentIDToMutableAllowancesForAgent { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_ALL_ALLOWANCES), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_ALL_ALLOWANCES.get_key_id(), TYPE_MAP); MapAgentIDToMutableAllowancesForAgent { obj_id: map_id } } pub fn balances(&self) -> MapAgentIDToMutableInt64 { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_BALANCES), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_BALANCES.get_key_id(), TYPE_MAP); MapAgentIDToMutableInt64 { obj_id: map_id } } pub fn supply(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_SUPPLY)) + ScMutableInt64::new(self.id, STATE_SUPPLY.get_key_id()) } } diff --git a/contracts/wasm/erc20/test/erc20_bg.wasm b/contracts/wasm/erc20/test/erc20_bg.wasm index 4ba488aa6c8e58aa1d0bff9dce738a7e2c6a1437..cfb81b91db4c394dfbc389d35e378c0bc6eccca6 100644 GIT binary patch delta 7285 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 ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_APPROVED)) + ScImmutableAgentID::new(self.id, PARAM_APPROVED.get_key_id()) } pub fn token_id(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -37,11 +37,11 @@ pub struct MutableApproveParams { impl MutableApproveParams { pub fn approved(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_APPROVED)) + ScMutableAgentID::new(self.id, PARAM_APPROVED.get_key_id()) } pub fn token_id(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -52,7 +52,7 @@ pub struct ImmutableBurnParams { impl ImmutableBurnParams { pub fn token_id(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -63,7 +63,7 @@ pub struct MutableBurnParams { impl MutableBurnParams { pub fn token_id(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -104,7 +104,7 @@ pub struct ImmutableMintParams { impl ImmutableMintParams { pub fn token_id(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -115,7 +115,7 @@ pub struct MutableMintParams { impl MutableMintParams { pub fn token_id(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -126,19 +126,19 @@ pub struct ImmutableSafeTransferFromParams { impl ImmutableSafeTransferFromParams { pub fn data(&self) -> ScImmutableBytes { - ScImmutableBytes::new(self.id, idx_map(IDX_PARAM_DATA)) + ScImmutableBytes::new(self.id, PARAM_DATA.get_key_id()) } pub fn from(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_FROM)) + ScImmutableAgentID::new(self.id, PARAM_FROM.get_key_id()) } pub fn to(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_TO)) + ScImmutableAgentID::new(self.id, PARAM_TO.get_key_id()) } pub fn token_id(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -149,19 +149,19 @@ pub struct MutableSafeTransferFromParams { impl MutableSafeTransferFromParams { pub fn data(&self) -> ScMutableBytes { - ScMutableBytes::new(self.id, idx_map(IDX_PARAM_DATA)) + ScMutableBytes::new(self.id, PARAM_DATA.get_key_id()) } pub fn from(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_FROM)) + ScMutableAgentID::new(self.id, PARAM_FROM.get_key_id()) } pub fn to(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_TO)) + ScMutableAgentID::new(self.id, PARAM_TO.get_key_id()) } pub fn token_id(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -172,11 +172,11 @@ pub struct ImmutableSetApprovalForAllParams { impl ImmutableSetApprovalForAllParams { pub fn approval(&self) -> ScImmutableBool { - ScImmutableBool::new(self.id, idx_map(IDX_PARAM_APPROVAL)) + ScImmutableBool::new(self.id, PARAM_APPROVAL.get_key_id()) } pub fn operator(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_OPERATOR)) + ScImmutableAgentID::new(self.id, PARAM_OPERATOR.get_key_id()) } } @@ -187,11 +187,11 @@ pub struct MutableSetApprovalForAllParams { impl MutableSetApprovalForAllParams { pub fn approval(&self) -> ScMutableBool { - ScMutableBool::new(self.id, idx_map(IDX_PARAM_APPROVAL)) + ScMutableBool::new(self.id, PARAM_APPROVAL.get_key_id()) } pub fn operator(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_OPERATOR)) + ScMutableAgentID::new(self.id, PARAM_OPERATOR.get_key_id()) } } @@ -202,15 +202,15 @@ pub struct ImmutableTransferFromParams { impl ImmutableTransferFromParams { pub fn from(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_FROM)) + ScImmutableAgentID::new(self.id, PARAM_FROM.get_key_id()) } pub fn to(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_TO)) + ScImmutableAgentID::new(self.id, PARAM_TO.get_key_id()) } pub fn token_id(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -221,15 +221,15 @@ pub struct MutableTransferFromParams { impl MutableTransferFromParams { pub fn from(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_FROM)) + ScMutableAgentID::new(self.id, PARAM_FROM.get_key_id()) } pub fn to(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_TO)) + ScMutableAgentID::new(self.id, PARAM_TO.get_key_id()) } pub fn token_id(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -240,7 +240,7 @@ pub struct ImmutableBalanceOfParams { impl ImmutableBalanceOfParams { pub fn owner(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_OWNER)) + ScImmutableAgentID::new(self.id, PARAM_OWNER.get_key_id()) } } @@ -251,7 +251,7 @@ pub struct MutableBalanceOfParams { impl MutableBalanceOfParams { pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_OWNER)) + ScMutableAgentID::new(self.id, PARAM_OWNER.get_key_id()) } } @@ -262,7 +262,7 @@ pub struct ImmutableGetApprovedParams { impl ImmutableGetApprovedParams { pub fn token_id(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -273,7 +273,7 @@ pub struct MutableGetApprovedParams { impl MutableGetApprovedParams { pub fn token_id(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -284,11 +284,11 @@ pub struct ImmutableIsApprovedForAllParams { impl ImmutableIsApprovedForAllParams { pub fn operator(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_OPERATOR)) + ScImmutableAgentID::new(self.id, PARAM_OPERATOR.get_key_id()) } pub fn owner(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_OWNER)) + ScImmutableAgentID::new(self.id, PARAM_OWNER.get_key_id()) } } @@ -299,11 +299,11 @@ pub struct MutableIsApprovedForAllParams { impl MutableIsApprovedForAllParams { pub fn operator(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_OPERATOR)) + ScMutableAgentID::new(self.id, PARAM_OPERATOR.get_key_id()) } pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_OWNER)) + ScMutableAgentID::new(self.id, PARAM_OWNER.get_key_id()) } } @@ -314,7 +314,7 @@ pub struct ImmutableOwnerOfParams { impl ImmutableOwnerOfParams { pub fn token_id(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -325,7 +325,7 @@ pub struct MutableOwnerOfParams { impl MutableOwnerOfParams { pub fn token_id(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -336,7 +336,7 @@ pub struct ImmutableTokenURIParams { impl ImmutableTokenURIParams { pub fn token_id(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } @@ -347,6 +347,6 @@ pub struct MutableTokenURIParams { impl MutableTokenURIParams { pub fn token_id(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_TOKEN_ID)) + ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } } diff --git a/contracts/wasm/erc721/src/results.rs b/contracts/wasm/erc721/src/results.rs index 7a62221715..12dc895e67 100644 --- a/contracts/wasm/erc721/src/results.rs +++ b/contracts/wasm/erc721/src/results.rs @@ -22,7 +22,7 @@ pub struct ImmutableBalanceOfResults { impl ImmutableBalanceOfResults { pub fn amount(&self) -> ScImmutableUint64 { - ScImmutableUint64::new(self.id, idx_map(IDX_RESULT_AMOUNT)) + ScImmutableUint64::new(self.id, RESULT_AMOUNT.get_key_id()) } } @@ -33,7 +33,7 @@ pub struct MutableBalanceOfResults { impl MutableBalanceOfResults { pub fn amount(&self) -> ScMutableUint64 { - ScMutableUint64::new(self.id, idx_map(IDX_RESULT_AMOUNT)) + ScMutableUint64::new(self.id, RESULT_AMOUNT.get_key_id()) } } @@ -44,7 +44,7 @@ pub struct ImmutableGetApprovedResults { impl ImmutableGetApprovedResults { pub fn approved(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_RESULT_APPROVED)) + ScImmutableAgentID::new(self.id, RESULT_APPROVED.get_key_id()) } } @@ -55,7 +55,7 @@ pub struct MutableGetApprovedResults { impl MutableGetApprovedResults { pub fn approved(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_RESULT_APPROVED)) + ScMutableAgentID::new(self.id, RESULT_APPROVED.get_key_id()) } } @@ -66,7 +66,7 @@ pub struct ImmutableIsApprovedForAllResults { impl ImmutableIsApprovedForAllResults { pub fn approval(&self) -> ScImmutableBool { - ScImmutableBool::new(self.id, idx_map(IDX_RESULT_APPROVAL)) + ScImmutableBool::new(self.id, RESULT_APPROVAL.get_key_id()) } } @@ -77,7 +77,7 @@ pub struct MutableIsApprovedForAllResults { impl MutableIsApprovedForAllResults { pub fn approval(&self) -> ScMutableBool { - ScMutableBool::new(self.id, idx_map(IDX_RESULT_APPROVAL)) + ScMutableBool::new(self.id, RESULT_APPROVAL.get_key_id()) } } @@ -88,7 +88,7 @@ pub struct ImmutableNameResults { impl ImmutableNameResults { pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_NAME)) + ScImmutableString::new(self.id, RESULT_NAME.get_key_id()) } } @@ -99,7 +99,7 @@ pub struct MutableNameResults { impl MutableNameResults { pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_NAME)) + ScMutableString::new(self.id, RESULT_NAME.get_key_id()) } } @@ -110,7 +110,7 @@ pub struct ImmutableOwnerOfResults { impl ImmutableOwnerOfResults { pub fn owner(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_RESULT_OWNER)) + ScImmutableAgentID::new(self.id, RESULT_OWNER.get_key_id()) } } @@ -121,7 +121,7 @@ pub struct MutableOwnerOfResults { impl MutableOwnerOfResults { pub fn owner(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_RESULT_OWNER)) + ScMutableAgentID::new(self.id, RESULT_OWNER.get_key_id()) } } @@ -132,7 +132,7 @@ pub struct ImmutableSymbolResults { impl ImmutableSymbolResults { pub fn symbol(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_SYMBOL)) + ScImmutableString::new(self.id, RESULT_SYMBOL.get_key_id()) } } @@ -143,7 +143,7 @@ pub struct MutableSymbolResults { impl MutableSymbolResults { pub fn symbol(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_SYMBOL)) + ScMutableString::new(self.id, RESULT_SYMBOL.get_key_id()) } } @@ -154,7 +154,7 @@ pub struct ImmutableTokenURIResults { impl ImmutableTokenURIResults { pub fn token_uri(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_TOKEN_URI)) + ScImmutableString::new(self.id, RESULT_TOKEN_URI.get_key_id()) } } @@ -165,6 +165,6 @@ pub struct MutableTokenURIResults { impl MutableTokenURIResults { pub fn token_uri(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_TOKEN_URI)) + ScMutableString::new(self.id, RESULT_TOKEN_URI.get_key_id()) } } diff --git a/contracts/wasm/erc721/src/state.rs b/contracts/wasm/erc721/src/state.rs index f48fc5e9cb..b438698ca5 100644 --- a/contracts/wasm/erc721/src/state.rs +++ b/contracts/wasm/erc721/src/state.rs @@ -56,31 +56,31 @@ pub struct ImmutableErc721State { impl ImmutableErc721State { pub fn approved_accounts(&self) -> MapHashToImmutableAgentID { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_APPROVED_ACCOUNTS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_APPROVED_ACCOUNTS.get_key_id(), TYPE_MAP); MapHashToImmutableAgentID { obj_id: map_id } } pub fn approved_operators(&self) -> MapAgentIDToImmutableOperators { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_APPROVED_OPERATORS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_APPROVED_OPERATORS.get_key_id(), TYPE_MAP); MapAgentIDToImmutableOperators { obj_id: map_id } } pub fn balances(&self) -> MapAgentIDToImmutableUint64 { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_BALANCES), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_BALANCES.get_key_id(), TYPE_MAP); MapAgentIDToImmutableUint64 { obj_id: map_id } } pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_STATE_NAME)) + ScImmutableString::new(self.id, STATE_NAME.get_key_id()) } pub fn owners(&self) -> MapHashToImmutableAgentID { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_OWNERS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_OWNERS.get_key_id(), TYPE_MAP); MapHashToImmutableAgentID { obj_id: map_id } } pub fn symbol(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_STATE_SYMBOL)) + ScImmutableString::new(self.id, STATE_SYMBOL.get_key_id()) } } @@ -141,30 +141,30 @@ impl MutableErc721State { } pub fn approved_accounts(&self) -> MapHashToMutableAgentID { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_APPROVED_ACCOUNTS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_APPROVED_ACCOUNTS.get_key_id(), TYPE_MAP); MapHashToMutableAgentID { obj_id: map_id } } pub fn approved_operators(&self) -> MapAgentIDToMutableOperators { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_APPROVED_OPERATORS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_APPROVED_OPERATORS.get_key_id(), TYPE_MAP); MapAgentIDToMutableOperators { obj_id: map_id } } pub fn balances(&self) -> MapAgentIDToMutableUint64 { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_BALANCES), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_BALANCES.get_key_id(), TYPE_MAP); MapAgentIDToMutableUint64 { obj_id: map_id } } pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_STATE_NAME)) + ScMutableString::new(self.id, STATE_NAME.get_key_id()) } pub fn owners(&self) -> MapHashToMutableAgentID { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_OWNERS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_OWNERS.get_key_id(), TYPE_MAP); MapHashToMutableAgentID { obj_id: map_id } } pub fn symbol(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_STATE_SYMBOL)) + ScMutableString::new(self.id, STATE_SYMBOL.get_key_id()) } } diff --git a/contracts/wasm/erc721/test/erc721_bg.wasm b/contracts/wasm/erc721/test/erc721_bg.wasm index 5b758abf0e4b9f148baac543063d37f1547441ef..3a7d05521ee3ff423ca25030176efe3b706c0316 100644 GIT binary patch delta 9757 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 delta 9160 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 diff --git a/contracts/wasm/erc721/ts/erc721/params.ts b/contracts/wasm/erc721/ts/erc721/params.ts index 92eaed30f6..23aff4200b 100644 --- a/contracts/wasm/erc721/ts/erc721/params.ts +++ b/contracts/wasm/erc721/ts/erc721/params.ts @@ -10,33 +10,33 @@ import * as sc from "./index"; export class ImmutableApproveParams extends wasmlib.ScMapID { approved(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamApproved]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamApproved)); } tokenID(): wasmlib.ScImmutableHash { - return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class MutableApproveParams extends wasmlib.ScMapID { approved(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamApproved]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamApproved)); } tokenID(): wasmlib.ScMutableHash { - return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class ImmutableBurnParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScImmutableHash { - return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class MutableBurnParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScMutableHash { - return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } @@ -62,164 +62,164 @@ export class MutableInitParams extends wasmlib.ScMapID { export class ImmutableMintParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScImmutableHash { - return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class MutableMintParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScMutableHash { - return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class ImmutableSafeTransferFromParams extends wasmlib.ScMapID { data(): wasmlib.ScImmutableBytes { - return new wasmlib.ScImmutableBytes(this.mapID, sc.idxMap[sc.IdxParamData]); + return new wasmlib.ScImmutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ParamData)); } from(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamFrom]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamFrom)); } to(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamTo]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamTo)); } tokenID(): wasmlib.ScImmutableHash { - return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class MutableSafeTransferFromParams extends wasmlib.ScMapID { data(): wasmlib.ScMutableBytes { - return new wasmlib.ScMutableBytes(this.mapID, sc.idxMap[sc.IdxParamData]); + return new wasmlib.ScMutableBytes(this.mapID, wasmlib.Key32.fromString(sc.ParamData)); } from(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamFrom]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamFrom)); } to(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamTo]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamTo)); } tokenID(): wasmlib.ScMutableHash { - return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class ImmutableSetApprovalForAllParams extends wasmlib.ScMapID { approval(): wasmlib.ScImmutableBool { - return new wasmlib.ScImmutableBool(this.mapID, sc.idxMap[sc.IdxParamApproval]); + return new wasmlib.ScImmutableBool(this.mapID, wasmlib.Key32.fromString(sc.ParamApproval)); } operator(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOperator]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamOperator)); } } export class MutableSetApprovalForAllParams extends wasmlib.ScMapID { approval(): wasmlib.ScMutableBool { - return new wasmlib.ScMutableBool(this.mapID, sc.idxMap[sc.IdxParamApproval]); + return new wasmlib.ScMutableBool(this.mapID, wasmlib.Key32.fromString(sc.ParamApproval)); } operator(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOperator]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamOperator)); } } export class ImmutableTransferFromParams extends wasmlib.ScMapID { from(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamFrom]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamFrom)); } to(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamTo]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamTo)); } tokenID(): wasmlib.ScImmutableHash { - return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class MutableTransferFromParams extends wasmlib.ScMapID { from(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamFrom]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamFrom)); } to(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamTo]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamTo)); } tokenID(): wasmlib.ScMutableHash { - return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class ImmutableBalanceOfParams extends wasmlib.ScMapID { owner(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOwner]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamOwner)); } } export class MutableBalanceOfParams extends wasmlib.ScMapID { owner(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOwner]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamOwner)); } } export class ImmutableGetApprovedParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScImmutableHash { - return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class MutableGetApprovedParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScMutableHash { - return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class ImmutableIsApprovedForAllParams extends wasmlib.ScMapID { operator(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOperator]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamOperator)); } owner(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOwner]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamOwner)); } } export class MutableIsApprovedForAllParams extends wasmlib.ScMapID { operator(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOperator]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamOperator)); } owner(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxParamOwner]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ParamOwner)); } } export class ImmutableOwnerOfParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScImmutableHash { - return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class MutableOwnerOfParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScMutableHash { - return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class ImmutableTokenURIParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScImmutableHash { - return new wasmlib.ScImmutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } export class MutableTokenURIParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScMutableHash { - return new wasmlib.ScMutableHash(this.mapID, sc.idxMap[sc.IdxParamTokenID]); + return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } } diff --git a/contracts/wasm/erc721/ts/erc721/results.ts b/contracts/wasm/erc721/ts/erc721/results.ts index 34f4237014..30edca4097 100644 --- a/contracts/wasm/erc721/ts/erc721/results.ts +++ b/contracts/wasm/erc721/ts/erc721/results.ts @@ -10,84 +10,84 @@ import * as sc from "./index"; export class ImmutableBalanceOfResults extends wasmlib.ScMapID { amount(): wasmlib.ScImmutableUint64 { - return new wasmlib.ScImmutableUint64(this.mapID, sc.idxMap[sc.IdxResultAmount]); + return new wasmlib.ScImmutableUint64(this.mapID, wasmlib.Key32.fromString(sc.ResultAmount)); } } export class MutableBalanceOfResults extends wasmlib.ScMapID { amount(): wasmlib.ScMutableUint64 { - return new wasmlib.ScMutableUint64(this.mapID, sc.idxMap[sc.IdxResultAmount]); + return new wasmlib.ScMutableUint64(this.mapID, wasmlib.Key32.fromString(sc.ResultAmount)); } } export class ImmutableGetApprovedResults extends wasmlib.ScMapID { approved(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultApproved]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultApproved)); } } export class MutableGetApprovedResults extends wasmlib.ScMapID { approved(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultApproved]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultApproved)); } } export class ImmutableIsApprovedForAllResults extends wasmlib.ScMapID { approval(): wasmlib.ScImmutableBool { - return new wasmlib.ScImmutableBool(this.mapID, sc.idxMap[sc.IdxResultApproval]); + return new wasmlib.ScImmutableBool(this.mapID, wasmlib.Key32.fromString(sc.ResultApproval)); } } export class MutableIsApprovedForAllResults extends wasmlib.ScMapID { approval(): wasmlib.ScMutableBool { - return new wasmlib.ScMutableBool(this.mapID, sc.idxMap[sc.IdxResultApproval]); + return new wasmlib.ScMutableBool(this.mapID, wasmlib.Key32.fromString(sc.ResultApproval)); } } export class ImmutableNameResults extends wasmlib.ScMapID { name(): wasmlib.ScImmutableString { - return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultName]); + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultName)); } } export class MutableNameResults extends wasmlib.ScMapID { name(): wasmlib.ScMutableString { - return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultName]); + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultName)); } } export class ImmutableOwnerOfResults extends wasmlib.ScMapID { owner(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultOwner]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultOwner)); } } export class MutableOwnerOfResults extends wasmlib.ScMapID { owner(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultOwner]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultOwner)); } } export class ImmutableSymbolResults extends wasmlib.ScMapID { symbol(): wasmlib.ScImmutableString { - return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultSymbol]); + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultSymbol)); } } export class MutableSymbolResults extends wasmlib.ScMapID { symbol(): wasmlib.ScMutableString { - return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultSymbol]); + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultSymbol)); } } export class ImmutableTokenURIResults extends wasmlib.ScMapID { tokenURI(): wasmlib.ScImmutableString { - return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultTokenURI]); + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultTokenURI)); } } export class MutableTokenURIResults extends wasmlib.ScMapID { tokenURI(): wasmlib.ScMutableString { - return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultTokenURI]); + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultTokenURI)); } } diff --git a/contracts/wasm/erc721/ts/erc721/state.ts b/contracts/wasm/erc721/ts/erc721/state.ts index 247afa6dc6..989fce026a 100644 --- a/contracts/wasm/erc721/ts/erc721/state.ts +++ b/contracts/wasm/erc721/ts/erc721/state.ts @@ -47,31 +47,31 @@ export class MapAgentIDToImmutableUint64 { export class ImmutableErc721State extends wasmlib.ScMapID { approvedAccounts(): sc.MapHashToImmutableAgentID { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateApprovedAccounts], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateApprovedAccounts), wasmlib.TYPE_MAP); return new sc.MapHashToImmutableAgentID(mapID); } approvedOperators(): sc.MapAgentIDToImmutableOperators { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateApprovedOperators], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateApprovedOperators), wasmlib.TYPE_MAP); return new sc.MapAgentIDToImmutableOperators(mapID); } balances(): sc.MapAgentIDToImmutableUint64 { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBalances], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateBalances), wasmlib.TYPE_MAP); return new sc.MapAgentIDToImmutableUint64(mapID); } name(): wasmlib.ScImmutableString { - return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxStateName]); + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.StateName)); } owners(): sc.MapHashToImmutableAgentID { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateOwners], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateOwners), wasmlib.TYPE_MAP); return new sc.MapHashToImmutableAgentID(mapID); } symbol(): wasmlib.ScImmutableString { - return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxStateSymbol]); + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.StateSymbol)); } } @@ -132,30 +132,30 @@ export class MutableErc721State extends wasmlib.ScMapID { } approvedAccounts(): sc.MapHashToMutableAgentID { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateApprovedAccounts], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateApprovedAccounts), wasmlib.TYPE_MAP); return new sc.MapHashToMutableAgentID(mapID); } approvedOperators(): sc.MapAgentIDToMutableOperators { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateApprovedOperators], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateApprovedOperators), wasmlib.TYPE_MAP); return new sc.MapAgentIDToMutableOperators(mapID); } balances(): sc.MapAgentIDToMutableUint64 { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBalances], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateBalances), wasmlib.TYPE_MAP); return new sc.MapAgentIDToMutableUint64(mapID); } name(): wasmlib.ScMutableString { - return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxStateName]); + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.StateName)); } owners(): sc.MapHashToMutableAgentID { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateOwners], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateOwners), wasmlib.TYPE_MAP); return new sc.MapHashToMutableAgentID(mapID); } symbol(): wasmlib.ScMutableString { - return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxStateSymbol]); + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.StateSymbol)); } } diff --git a/contracts/wasm/fairauction/go/fairauction/params.go b/contracts/wasm/fairauction/go/fairauction/params.go index 7e85c051fb..2174a6361a 100644 --- a/contracts/wasm/fairauction/go/fairauction/params.go +++ b/contracts/wasm/fairauction/go/fairauction/params.go @@ -14,7 +14,7 @@ type ImmutableFinalizeAuctionParams struct { } func (s ImmutableFinalizeAuctionParams) Color() wasmlib.ScImmutableColor { - return wasmlib.NewScImmutableColor(s.id, idxMap[IdxParamColor]) + return wasmlib.NewScImmutableColor(s.id, wasmlib.KeyID(ParamColor)) } type MutableFinalizeAuctionParams struct { @@ -22,7 +22,7 @@ type MutableFinalizeAuctionParams struct { } func (s MutableFinalizeAuctionParams) Color() wasmlib.ScMutableColor { - return wasmlib.NewScMutableColor(s.id, idxMap[IdxParamColor]) + return wasmlib.NewScMutableColor(s.id, wasmlib.KeyID(ParamColor)) } type ImmutablePlaceBidParams struct { @@ -30,7 +30,7 @@ type ImmutablePlaceBidParams struct { } func (s ImmutablePlaceBidParams) Color() wasmlib.ScImmutableColor { - return wasmlib.NewScImmutableColor(s.id, idxMap[IdxParamColor]) + return wasmlib.NewScImmutableColor(s.id, wasmlib.KeyID(ParamColor)) } type MutablePlaceBidParams struct { @@ -38,7 +38,7 @@ type MutablePlaceBidParams struct { } func (s MutablePlaceBidParams) Color() wasmlib.ScMutableColor { - return wasmlib.NewScMutableColor(s.id, idxMap[IdxParamColor]) + return wasmlib.NewScMutableColor(s.id, wasmlib.KeyID(ParamColor)) } type ImmutableSetOwnerMarginParams struct { @@ -46,7 +46,7 @@ type ImmutableSetOwnerMarginParams struct { } func (s ImmutableSetOwnerMarginParams) OwnerMargin() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxParamOwnerMargin]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ParamOwnerMargin)) } type MutableSetOwnerMarginParams struct { @@ -54,7 +54,7 @@ type MutableSetOwnerMarginParams struct { } func (s MutableSetOwnerMarginParams) OwnerMargin() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxParamOwnerMargin]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ParamOwnerMargin)) } type ImmutableStartAuctionParams struct { @@ -62,19 +62,19 @@ type ImmutableStartAuctionParams struct { } func (s ImmutableStartAuctionParams) Color() wasmlib.ScImmutableColor { - return wasmlib.NewScImmutableColor(s.id, idxMap[IdxParamColor]) + return wasmlib.NewScImmutableColor(s.id, wasmlib.KeyID(ParamColor)) } func (s ImmutableStartAuctionParams) Description() wasmlib.ScImmutableString { - return wasmlib.NewScImmutableString(s.id, idxMap[IdxParamDescription]) + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamDescription)) } func (s ImmutableStartAuctionParams) Duration() wasmlib.ScImmutableInt32 { - return wasmlib.NewScImmutableInt32(s.id, idxMap[IdxParamDuration]) + return wasmlib.NewScImmutableInt32(s.id, wasmlib.KeyID(ParamDuration)) } func (s ImmutableStartAuctionParams) MinimumBid() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxParamMinimumBid]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ParamMinimumBid)) } type MutableStartAuctionParams struct { @@ -82,19 +82,19 @@ type MutableStartAuctionParams struct { } func (s MutableStartAuctionParams) Color() wasmlib.ScMutableColor { - return wasmlib.NewScMutableColor(s.id, idxMap[IdxParamColor]) + return wasmlib.NewScMutableColor(s.id, wasmlib.KeyID(ParamColor)) } func (s MutableStartAuctionParams) Description() wasmlib.ScMutableString { - return wasmlib.NewScMutableString(s.id, idxMap[IdxParamDescription]) + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamDescription)) } func (s MutableStartAuctionParams) Duration() wasmlib.ScMutableInt32 { - return wasmlib.NewScMutableInt32(s.id, idxMap[IdxParamDuration]) + return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(ParamDuration)) } func (s MutableStartAuctionParams) MinimumBid() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxParamMinimumBid]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ParamMinimumBid)) } type ImmutableGetInfoParams struct { @@ -102,7 +102,7 @@ type ImmutableGetInfoParams struct { } func (s ImmutableGetInfoParams) Color() wasmlib.ScImmutableColor { - return wasmlib.NewScImmutableColor(s.id, idxMap[IdxParamColor]) + return wasmlib.NewScImmutableColor(s.id, wasmlib.KeyID(ParamColor)) } type MutableGetInfoParams struct { @@ -110,5 +110,5 @@ type MutableGetInfoParams struct { } func (s MutableGetInfoParams) Color() wasmlib.ScMutableColor { - return wasmlib.NewScMutableColor(s.id, idxMap[IdxParamColor]) + return wasmlib.NewScMutableColor(s.id, wasmlib.KeyID(ParamColor)) } diff --git a/contracts/wasm/fairauction/go/fairauction/results.go b/contracts/wasm/fairauction/go/fairauction/results.go index 3e10283f12..033177634d 100644 --- a/contracts/wasm/fairauction/go/fairauction/results.go +++ b/contracts/wasm/fairauction/go/fairauction/results.go @@ -14,51 +14,51 @@ type ImmutableGetInfoResults struct { } func (s ImmutableGetInfoResults) Bidders() wasmlib.ScImmutableInt32 { - return wasmlib.NewScImmutableInt32(s.id, idxMap[IdxResultBidders]) + return wasmlib.NewScImmutableInt32(s.id, wasmlib.KeyID(ResultBidders)) } func (s ImmutableGetInfoResults) Color() wasmlib.ScImmutableColor { - return wasmlib.NewScImmutableColor(s.id, idxMap[IdxResultColor]) + return wasmlib.NewScImmutableColor(s.id, wasmlib.KeyID(ResultColor)) } func (s ImmutableGetInfoResults) Creator() wasmlib.ScImmutableAgentID { - return wasmlib.NewScImmutableAgentID(s.id, idxMap[IdxResultCreator]) + return wasmlib.NewScImmutableAgentID(s.id, wasmlib.KeyID(ResultCreator)) } func (s ImmutableGetInfoResults) Deposit() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultDeposit]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultDeposit)) } func (s ImmutableGetInfoResults) Description() wasmlib.ScImmutableString { - return wasmlib.NewScImmutableString(s.id, idxMap[IdxResultDescription]) + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ResultDescription)) } func (s ImmutableGetInfoResults) Duration() wasmlib.ScImmutableInt32 { - return wasmlib.NewScImmutableInt32(s.id, idxMap[IdxResultDuration]) + return wasmlib.NewScImmutableInt32(s.id, wasmlib.KeyID(ResultDuration)) } func (s ImmutableGetInfoResults) HighestBid() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultHighestBid]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultHighestBid)) } func (s ImmutableGetInfoResults) HighestBidder() wasmlib.ScImmutableAgentID { - return wasmlib.NewScImmutableAgentID(s.id, idxMap[IdxResultHighestBidder]) + return wasmlib.NewScImmutableAgentID(s.id, wasmlib.KeyID(ResultHighestBidder)) } func (s ImmutableGetInfoResults) MinimumBid() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultMinimumBid]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultMinimumBid)) } func (s ImmutableGetInfoResults) NumTokens() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultNumTokens]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultNumTokens)) } func (s ImmutableGetInfoResults) OwnerMargin() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultOwnerMargin]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultOwnerMargin)) } func (s ImmutableGetInfoResults) WhenStarted() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultWhenStarted]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultWhenStarted)) } type MutableGetInfoResults struct { @@ -66,49 +66,49 @@ type MutableGetInfoResults struct { } func (s MutableGetInfoResults) Bidders() wasmlib.ScMutableInt32 { - return wasmlib.NewScMutableInt32(s.id, idxMap[IdxResultBidders]) + return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(ResultBidders)) } func (s MutableGetInfoResults) Color() wasmlib.ScMutableColor { - return wasmlib.NewScMutableColor(s.id, idxMap[IdxResultColor]) + return wasmlib.NewScMutableColor(s.id, wasmlib.KeyID(ResultColor)) } func (s MutableGetInfoResults) Creator() wasmlib.ScMutableAgentID { - return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxResultCreator]) + return wasmlib.NewScMutableAgentID(s.id, wasmlib.KeyID(ResultCreator)) } func (s MutableGetInfoResults) Deposit() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultDeposit]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultDeposit)) } func (s MutableGetInfoResults) Description() wasmlib.ScMutableString { - return wasmlib.NewScMutableString(s.id, idxMap[IdxResultDescription]) + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ResultDescription)) } func (s MutableGetInfoResults) Duration() wasmlib.ScMutableInt32 { - return wasmlib.NewScMutableInt32(s.id, idxMap[IdxResultDuration]) + return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(ResultDuration)) } func (s MutableGetInfoResults) HighestBid() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultHighestBid]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultHighestBid)) } func (s MutableGetInfoResults) HighestBidder() wasmlib.ScMutableAgentID { - return wasmlib.NewScMutableAgentID(s.id, idxMap[IdxResultHighestBidder]) + return wasmlib.NewScMutableAgentID(s.id, wasmlib.KeyID(ResultHighestBidder)) } func (s MutableGetInfoResults) MinimumBid() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultMinimumBid]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultMinimumBid)) } func (s MutableGetInfoResults) NumTokens() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultNumTokens]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultNumTokens)) } func (s MutableGetInfoResults) OwnerMargin() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultOwnerMargin]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultOwnerMargin)) } func (s MutableGetInfoResults) WhenStarted() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultWhenStarted]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultWhenStarted)) } diff --git a/contracts/wasm/fairauction/go/fairauction/state.go b/contracts/wasm/fairauction/go/fairauction/state.go index fc21692cc0..4fce8d8fba 100644 --- a/contracts/wasm/fairauction/go/fairauction/state.go +++ b/contracts/wasm/fairauction/go/fairauction/state.go @@ -40,22 +40,22 @@ type ImmutableFairAuctionState struct { } func (s ImmutableFairAuctionState) Auctions() MapColorToImmutableAuction { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateAuctions], wasmlib.TYPE_MAP) + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateAuctions), wasmlib.TYPE_MAP) return MapColorToImmutableAuction{objID: mapID} } func (s ImmutableFairAuctionState) BidderList() MapColorToImmutableBidderList { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBidderList], wasmlib.TYPE_MAP) + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateBidderList), wasmlib.TYPE_MAP) return MapColorToImmutableBidderList{objID: mapID} } func (s ImmutableFairAuctionState) Bids() MapColorToImmutableBids { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBids], wasmlib.TYPE_MAP) + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateBids), wasmlib.TYPE_MAP) return MapColorToImmutableBids{objID: mapID} } func (s ImmutableFairAuctionState) OwnerMargin() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxStateOwnerMargin]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(StateOwnerMargin)) } type MapColorToMutableAuction struct { @@ -105,20 +105,20 @@ func (s MutableFairAuctionState) AsImmutable() ImmutableFairAuctionState { } func (s MutableFairAuctionState) Auctions() MapColorToMutableAuction { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateAuctions], wasmlib.TYPE_MAP) + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateAuctions), wasmlib.TYPE_MAP) return MapColorToMutableAuction{objID: mapID} } func (s MutableFairAuctionState) BidderList() MapColorToMutableBidderList { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBidderList], wasmlib.TYPE_MAP) + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateBidderList), wasmlib.TYPE_MAP) return MapColorToMutableBidderList{objID: mapID} } func (s MutableFairAuctionState) Bids() MapColorToMutableBids { - mapID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBids], wasmlib.TYPE_MAP) + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateBids), wasmlib.TYPE_MAP) return MapColorToMutableBids{objID: mapID} } func (s MutableFairAuctionState) OwnerMargin() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateOwnerMargin]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(StateOwnerMargin)) } diff --git a/contracts/wasm/fairauction/src/params.rs b/contracts/wasm/fairauction/src/params.rs index 8dcbe76892..bcc902b795 100644 --- a/contracts/wasm/fairauction/src/params.rs +++ b/contracts/wasm/fairauction/src/params.rs @@ -23,7 +23,7 @@ pub struct ImmutableFinalizeAuctionParams { impl ImmutableFinalizeAuctionParams { pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScImmutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -34,7 +34,7 @@ pub struct MutableFinalizeAuctionParams { impl MutableFinalizeAuctionParams { pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScMutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -45,7 +45,7 @@ pub struct ImmutablePlaceBidParams { impl ImmutablePlaceBidParams { pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScImmutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -56,7 +56,7 @@ pub struct MutablePlaceBidParams { impl MutablePlaceBidParams { pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScMutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -67,7 +67,7 @@ pub struct ImmutableSetOwnerMarginParams { impl ImmutableSetOwnerMarginParams { pub fn owner_margin(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_OWNER_MARGIN)) + ScImmutableInt64::new(self.id, PARAM_OWNER_MARGIN.get_key_id()) } } @@ -78,7 +78,7 @@ pub struct MutableSetOwnerMarginParams { impl MutableSetOwnerMarginParams { pub fn owner_margin(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_OWNER_MARGIN)) + ScMutableInt64::new(self.id, PARAM_OWNER_MARGIN.get_key_id()) } } @@ -89,19 +89,19 @@ pub struct ImmutableStartAuctionParams { impl ImmutableStartAuctionParams { pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScImmutableColor::new(self.id, PARAM_COLOR.get_key_id()) } pub fn description(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_DESCRIPTION)) + ScImmutableString::new(self.id, PARAM_DESCRIPTION.get_key_id()) } pub fn duration(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_DURATION)) + ScImmutableInt32::new(self.id, PARAM_DURATION.get_key_id()) } pub fn minimum_bid(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_MINIMUM_BID)) + ScImmutableInt64::new(self.id, PARAM_MINIMUM_BID.get_key_id()) } } @@ -112,19 +112,19 @@ pub struct MutableStartAuctionParams { impl MutableStartAuctionParams { pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScMutableColor::new(self.id, PARAM_COLOR.get_key_id()) } pub fn description(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_DESCRIPTION)) + ScMutableString::new(self.id, PARAM_DESCRIPTION.get_key_id()) } pub fn duration(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_DURATION)) + ScMutableInt32::new(self.id, PARAM_DURATION.get_key_id()) } pub fn minimum_bid(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_MINIMUM_BID)) + ScMutableInt64::new(self.id, PARAM_MINIMUM_BID.get_key_id()) } } @@ -135,7 +135,7 @@ pub struct ImmutableGetInfoParams { impl ImmutableGetInfoParams { pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScImmutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -146,6 +146,6 @@ pub struct MutableGetInfoParams { impl MutableGetInfoParams { pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScMutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } diff --git a/contracts/wasm/fairauction/src/results.rs b/contracts/wasm/fairauction/src/results.rs index 9c97fda447..649bd85b43 100644 --- a/contracts/wasm/fairauction/src/results.rs +++ b/contracts/wasm/fairauction/src/results.rs @@ -23,51 +23,51 @@ pub struct ImmutableGetInfoResults { impl ImmutableGetInfoResults { pub fn bidders(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_RESULT_BIDDERS)) + ScImmutableInt32::new(self.id, RESULT_BIDDERS.get_key_id()) } pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_RESULT_COLOR)) + ScImmutableColor::new(self.id, RESULT_COLOR.get_key_id()) } pub fn creator(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_RESULT_CREATOR)) + ScImmutableAgentID::new(self.id, RESULT_CREATOR.get_key_id()) } pub fn deposit(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_DEPOSIT)) + ScImmutableInt64::new(self.id, RESULT_DEPOSIT.get_key_id()) } pub fn description(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_DESCRIPTION)) + ScImmutableString::new(self.id, RESULT_DESCRIPTION.get_key_id()) } pub fn duration(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_RESULT_DURATION)) + ScImmutableInt32::new(self.id, RESULT_DURATION.get_key_id()) } pub fn highest_bid(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_HIGHEST_BID)) + ScImmutableInt64::new(self.id, RESULT_HIGHEST_BID.get_key_id()) } pub fn highest_bidder(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_RESULT_HIGHEST_BIDDER)) + ScImmutableAgentID::new(self.id, RESULT_HIGHEST_BIDDER.get_key_id()) } pub fn minimum_bid(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_MINIMUM_BID)) + ScImmutableInt64::new(self.id, RESULT_MINIMUM_BID.get_key_id()) } pub fn num_tokens(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_NUM_TOKENS)) + ScImmutableInt64::new(self.id, RESULT_NUM_TOKENS.get_key_id()) } pub fn owner_margin(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_OWNER_MARGIN)) + ScImmutableInt64::new(self.id, RESULT_OWNER_MARGIN.get_key_id()) } pub fn when_started(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_WHEN_STARTED)) + ScImmutableInt64::new(self.id, RESULT_WHEN_STARTED.get_key_id()) } } @@ -78,50 +78,50 @@ pub struct MutableGetInfoResults { impl MutableGetInfoResults { pub fn bidders(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_RESULT_BIDDERS)) + ScMutableInt32::new(self.id, RESULT_BIDDERS.get_key_id()) } pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_RESULT_COLOR)) + ScMutableColor::new(self.id, RESULT_COLOR.get_key_id()) } pub fn creator(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_RESULT_CREATOR)) + ScMutableAgentID::new(self.id, RESULT_CREATOR.get_key_id()) } pub fn deposit(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_DEPOSIT)) + ScMutableInt64::new(self.id, RESULT_DEPOSIT.get_key_id()) } pub fn description(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_DESCRIPTION)) + ScMutableString::new(self.id, RESULT_DESCRIPTION.get_key_id()) } pub fn duration(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_RESULT_DURATION)) + ScMutableInt32::new(self.id, RESULT_DURATION.get_key_id()) } pub fn highest_bid(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_HIGHEST_BID)) + ScMutableInt64::new(self.id, RESULT_HIGHEST_BID.get_key_id()) } pub fn highest_bidder(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_RESULT_HIGHEST_BIDDER)) + ScMutableAgentID::new(self.id, RESULT_HIGHEST_BIDDER.get_key_id()) } pub fn minimum_bid(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_MINIMUM_BID)) + ScMutableInt64::new(self.id, RESULT_MINIMUM_BID.get_key_id()) } pub fn num_tokens(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_NUM_TOKENS)) + ScMutableInt64::new(self.id, RESULT_NUM_TOKENS.get_key_id()) } pub fn owner_margin(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_OWNER_MARGIN)) + ScMutableInt64::new(self.id, RESULT_OWNER_MARGIN.get_key_id()) } pub fn when_started(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_WHEN_STARTED)) + ScMutableInt64::new(self.id, RESULT_WHEN_STARTED.get_key_id()) } } diff --git a/contracts/wasm/fairauction/src/state.rs b/contracts/wasm/fairauction/src/state.rs index 180b7ac68c..8f7b0007f1 100644 --- a/contracts/wasm/fairauction/src/state.rs +++ b/contracts/wasm/fairauction/src/state.rs @@ -58,22 +58,22 @@ pub struct ImmutableFairAuctionState { impl ImmutableFairAuctionState { pub fn auctions(&self) -> MapColorToImmutableAuction { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_AUCTIONS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_AUCTIONS.get_key_id(), TYPE_MAP); MapColorToImmutableAuction { obj_id: map_id } } pub fn bidder_list(&self) -> MapColorToImmutableBidderList { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_BIDDER_LIST), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_BIDDER_LIST.get_key_id(), TYPE_MAP); MapColorToImmutableBidderList { obj_id: map_id } } pub fn bids(&self) -> MapColorToImmutableBids { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_BIDS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_BIDS.get_key_id(), TYPE_MAP); MapColorToImmutableBids { obj_id: map_id } } pub fn owner_margin(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_OWNER_MARGIN)) + ScImmutableInt64::new(self.id, STATE_OWNER_MARGIN.get_key_id()) } } @@ -135,21 +135,21 @@ impl MutableFairAuctionState { } pub fn auctions(&self) -> MapColorToMutableAuction { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_AUCTIONS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_AUCTIONS.get_key_id(), TYPE_MAP); MapColorToMutableAuction { obj_id: map_id } } pub fn bidder_list(&self) -> MapColorToMutableBidderList { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_BIDDER_LIST), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_BIDDER_LIST.get_key_id(), TYPE_MAP); MapColorToMutableBidderList { obj_id: map_id } } pub fn bids(&self) -> MapColorToMutableBids { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_BIDS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_BIDS.get_key_id(), TYPE_MAP); MapColorToMutableBids { obj_id: map_id } } pub fn owner_margin(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_OWNER_MARGIN)) + ScMutableInt64::new(self.id, STATE_OWNER_MARGIN.get_key_id()) } } diff --git a/contracts/wasm/fairauction/test/fairauction_bg.wasm b/contracts/wasm/fairauction/test/fairauction_bg.wasm index 3f26ba1243a0df889582a7e01dfef512221ecce2..e2ded07e63fd10a941fd9c35f6ee585b487a33a7 100644 GIT binary patch delta 8736 zcma)C3vg6L*1p|$9to34ZW0ngAmPr0M}!bw8W11QJOXF}K|%0=fFwdD5+DSS=R^V$ z1R>qnthwq(`arT$;n|2y~I83x4Kg_?W% z^f}#q`t*5q!|9LtmuGo(N9-B)KC>xml*;YhRNFJ(+{RTVGuUFelRfa@88-Y38+?W> z{{uV2cCn-xr`ZNi*1`t1{`VO68her5mHa9TfOwiEWZrj(4VZb1b*tRX-)Fz~aLvy) zWGF%_@w)mdd@LWUE8_2Lnr*C5v=&*AH9(f|Ox|`>*6{+~aZFy|&-ZLQ+M;A~Wtmr* z=vH+_dx$^u&|2YAeOJ8O}z*oG0( zY3nnfquH1*-|f^LZYHehX?Eu|vwQ7%n0ATXJIEU0WfR>toe6cN7oT!ruI3b7=7b(Q zt$M;fd_)I{0g=595oJJ*Z5rHOWs+^I4m5|y=!(vBcvK8?(KwHz7sR~7;pH7i<$On? zKZ2iy_*t00MZq9rA7X>fS1rroic42@n|6>H;3V@pbPg$XTZxxz+nES$LV*AZ?E#+~ z?7`3-CYC=q4E4o12kW#!-GL>HNl0o%SG5-AQDYp2@DDi$PcoRWFN;Y<3tPipA(c<7 zq$Y-VRX20d!JB`tN~PPTzzRB>WxKNn6miix+JBLjb!Bp!x0|3gH0tw01&X)?%=nO! z6&?D~+1dh=aY1rbkh+Tdk4lQMqJ1nLNs?xD{87%@sa2oCYb02~9+4I~j>V zdl6&8o53m`b6bM?==`uDV?Txp&|%X~GK)tyMC@voT~R5s3?8z z$ADb1CQ3!_F+6ZZ}CPvS|#d*MQk5}z-J z+eF22Il%3iZF#chhcD|2xvmY95d+OkF3CdPjt~GzeoF>MGTdnCaW_~RTdk<5AP?H_G9vi1{Z7quAf!I_NH{D6WpVG$cILdvi;?hT3kw&*& z7u$I#q9ar?0accJm1UHK5DTr`b2cv9ZwyCNq?L3h?UvLW(ltW3mkeJ=iWIC2r=Z)- z1fZIM)Eq)NCKS>mhamri_@O6jGoENGMDwxgSh-HS0|xQ7=818dZt-L z{0FD>aKf;mmg&PfZ24~Ubo75`pHS-;34Y#OFCR>NfPcJ89!tz2@LggPffu^pL7=O9 zC4r)(n+Q}V4diXR8FrnEFrosv7)c2uL}?9CPy(1p0Vc3r zOW`6A$z~uSwyTsr7}OZaXCNWI#(nay$?0*;AqJAMnfiJpb1I9t=!hhB?30svEMo7= zrXK0h=PGgVcCDGo&w8wl{wHDG2%D9nM}J3H6k!{HohQr%EcjB&Lo9mtCJU8Osq<5x zAdFarTYH^(s3>8gUGZt}$|q8f@%GB#=wA6OVznK;|KV)gg+W*L$!%$W;SCLPReCA! zYzSUR|A_IX7vvFr67Se0f7a8q_Ym9=+q#T%lU*6a_!44xNN} z$q+4tx=g5(+}L}wT;`b-4jE@X1Mjt5P-j$*vWSsJQ#W$lHO|b<$_tAzICGp8Ck#F- zPRKlO%5X~rUCBkHGA1xsjCzV%E<|@DcvEK6v`VGdE0+Z5Nk?*!c;|_=-vU~O?D2@Ab_K5J&qzuV0WJs3x3@Ng;FxMaCh2di&JY?_)Z_n%8itK@ju_f@T zM@I~qCduh+A*Zt_mIuNy#8#q{HychFrh6?96#?$k))`Y$>xAqYaRHtxKfL)X0*7u{ zBi|lU!yER?sYA!`@Au1{LsJQSGAv8CA+Esr?8vF#?{87ybS@WG{N)#QnMW11FUhpw zgPk89Y*BLE4mo{zUpZ}2-)Ue@X)B09*6kwAgz#I*GObeRK%<)Ze%C% zI=dV9QZDKmvFyksljWMVPQOBH@e7A!-te3K7Y>I?Reg=tZ!XE|oI@Z_OZ?YbCaJ!$=RlbWO9rq=ZABrj~GX`1lYDArq^BX5PisItN zoImrnO8Lmhb+`ZOAc-M-kW6IyaBsT7c^dO1kJ`*m$of%tVzyxCsD)AdKp;3#R50H0 zy!>F?4Bi-!Y2zQsY7StZPQ|s^!EAnr-X0eHqC&D?Bl;`jJ=x-h)_0kLIxey~ycWht z(WIJ62~}PhKgh*J*G4GS$R}A7`U3x&a3^d~-f!TS!uXRDGWv7z1L2fav^_K(yGflq zQf-By!_4t%Wj?PeKb|lEQ~irrjdb+$1TXL>!^tXhBH`79yJ@L=CuRVDE=>0u^n-*) z6a7^KZz7zQmY*5;J0ZL#H?5HF%D;9XbMqqPK`_kEL;DHKuUPW8a%feXprJg|wqR#a zxUa9``jZXnN7O|TC)eZ->T`YWLScGM?mC?tnY)tY$Vo}z{Kbvul>Z=qL5oz>8_WJi z{@OPwwMRHrP}YQUtn4M*Cq<@|r0$di^@xm1nMF>U+}C=6GE;~2Po^i7;NHn~oVkO; z?%vJ#`NQ&$c^(3P&Ff9TmEXI^`NQUWk#Lpk{O(>}mEN~NyEA_%zj#kYK+sSQcWh_u#1KeriwnI>98C-buf}l>D1NiJ$Z3zKYU}SRq6=W zP(4m$Ep!2^#g)uxs#I1Lp<~FA*s|(FQr;Yw-*Z zF?0(VDn1GX#q>3dSMQbY-ZPmUmyUb&oa4AeB3_Yes+dSCGy%FPETRJ~XSfw*wSo`b zo6PuA&&u){I)Pm?W)rwPqd$R^nf<7q9W&FDPQ^K-@T&9>6Y=_@pteDEv0-LzN(-~D zYsQ6)lC;NZlq{Vyr=^E1g$e>qr;F4z1~cV8FGn@8_`bsA4qTai9#vQ9E@%G5qocfV z-_3aF45rQUGG6tp6tm}8zhb7Uj*dj zxdTI^8syc(5046dGdD}^^9*MW3d?vLMnVxxJSK){9Cht15hogI<*)^b!}n7{j}uIr zhj0?(6K@b61^cq_QlDy{`aX&eDPd3J4RT63rhR1O#evPS`OcLhd<@EX- zuNr2gA@-sifb)y$S}Sf4Qc){w7JYr^X)-GMV1!_JXHhIutu_mGL01Jq$haqPP1Z)L z^t>x->CL0|xI9|ie|#&Z&EehYu4l9~(QMA=<-(tb`kheh0Ao!9d^DKnOJe+)BXU8>lEhC9)6-eJ`Jk+d{|W@ZF4@6&DDBN$z}g2C+l zOM1o{mL9fe`SOxmGMgAo8FutXXDc_=I2K$Oi)d_(i5g+-YnD!0l(%W@Mh)sTp~5pr9|YBx!F>*JGU z+p@Vy2%U5(&h%+b%tLVkt>bcRnRJKa4igAUU1k1X^`r&2F1NG9P1`VK0;JV_;?PrQ z2e!!<%GW2hZok^!Mm(L{<**fN6JNf*zj?bnwIYwV)=KxvY<{jz-m$VTZ>f_DR<0O% ziI@kOlLwwgyLt!IG#PfxGL?Ar4IkM+P-8Mi7m;{GsnRr1)#gHlEKb{|$H zYLyhs2Qz>wTef1U-li%wpXLyqTji!z`>(1J{&!WCwY+M#Jp1@985=k=|5?iv1k=I( z3_l7Vo!q_J6Y*ehYV~kwTRkF??*BPFMd#z#Nc@8k7wx;`U8}R?-&Ws}xaa!wJ5nQW zT{A23^!5FnHS)zZxfH>^U6aZW?3B*6#k_O3{O`4?a{Jop{Kvg$mhi@U1Dqplc<|eG zD&81s<>mFW`MEvvt_?H&=k^%hiOi}&5}h=>+;kjE!T(3<>0w$h99YxYxJrbA7U(bv zn^vzD{x?wXCPop>I*z@#;ov3IrzMISvYk{+dK-F-MSa&ST1V!cVk~N`rjJyyFh#viCj0;_%>cG88>IMNeT|r->S3 zF+-zqx{v~ax(pHg(@k>46SF3T!nF;r-;LHfHd^NZtz*MF7KaD_AVhr#wJ6=Nj&3}Q z6BR7M#EtRn>hXtc4#z*Tas!nw$0`f?mAYWsrqL{;qZVTZiH17(2VLPXXOvmfSd^wZ zxn^^JerQ{8|K^PhMPR=z+hPJJ%IFQ33wq?+lF2&*!Ed$x31+s z?hL-MH5!3-n>_K0WFdn4gXjhb?lTKCH zw!M-c9TA-VbSCrD9l_Ne^+<7HVNu!g1xp@VQnsM1Xny&UvcjUJOO_Xx7ZxpFP*%LO zym(1z;fk{P1Q!)ATvW8Yd|Ywi)fPrCE-o!zykarBOIIwOv81G^bor81rA1}=^UD?% zm#$h=RC;gu{Ic?*LPhxnfBuHwmx=foi$-wD4|k-=VLOvuk$u~DobW5k16)(=ilWQ~ zbOHJRqX5eQy8*7V`nLc&frt2aSa_WUuODBJej5KWpgUr|An@qWy#3HPfeu>u13+5% zEFca19MA^1j?2I7Oy^ZP3aUwewR%gVX|_`YU*KdoDv z@K&_BR$s8v9^Q(U^LFh>sO+XGy@7tq6r~SfWw)B$Po_q>%-)_{F%TfTeqK)8+mDZmlaKAqeq>Rcrleu$M*Mo>7s6MgP5mK!{dIUbaFRDf z_gzOX3e)9Jdvg+g#xiu&QJN`Qo=Z-sADB7{Xe_#O0qMl=0;G+ATvb2X8yd9^-Tl$M zACL^?RX~TycD_D4z5^)P6WI=(PDe1~IltYzFj-SFf!>WD!4N(RZ8Cv{$-&e6m#Z!c QQ7-&X5X3||?&XdD4>&RB8vp`CL~~h;4do~jdTXG3c_f1WffV?EQevooMFHF-s`55VUBY+ z{chd5x2kSc-TT$83g`Y^wtXtAK2K^B9|)JG$LrFa;L@wVxm^mKvc;2BEoP2t6SuaB z!ENH1e-UkBmq@$sL$S`EzpzoP{WY2$60e9md%h{^A^cFJRGU2?jtKSCGfzFrkAC$(IuEe~iDe7dO_uZcxUmMTd1>&3GEDE(2- z62~ddH9D~hszHC845{)Ky4#g0L7sH|99_TZ8p_O8HLJghfYx&jG`%b02~fCbQ06FBR` zONO<@VvEVWS@No_Hvya7jG|xDF<0ZDFyo8ap>kz>K=WzdM3*@ZBDYHiH4IufQ)wbRU;bRwgkuZ&_9b=~1kuZ&_ zwK3D`NSLzP6wA8P%$;;ZOrv^dnK|W1m`2skGjqm~Fjc2$dbi_7)e#iS@!@G}fUn9# z|4_uWW(AL_Ee>cL3T}ilTvj&|f@a)eQ!X~eViqziAV}$euLq+1h|iyT+=^a=P~75R zut>Vp$)l9(^FQcBvJpZon;M6%3!Sk7_sk(FBGC>AfF*y=2EjJmZ0NB!85)x-D=Xs& zPLy7I4mLX2c0=sg6|iESPNrlIvSJ+C9L_fa9bt|@w@?X%gdogqK1#EbrVfOqan@@2 z0gi09usKEsYZ(G=#uNH1$?tPU!?^-bB9VNzr<@%Zm^(9>Pr~Ptvqm`>W4bzFdeAQ| zAh*^ELtqh01KM&vpn!t*xkP7@3v7r29`aD8hv&%)99d<6cYycW#DS^A(^!AgZR4J9 zL#W+^&z1<7$2P&a6Vb;CIkWM1bHG`S(Z|qQ;}>v+_Y8k`OeD$lnTCy}>|B2`{Ke4) zf(r!%%aZMlar4INi=+M(a7Q-DOlFIdO}A~a^h2Xdv3eF0(Yv&lzU!Hl+!0Y=8#}lzy{Gg7dZcQIn8KoleKaY3k@yw8 zlb+=%3tMqPpQo?!R5;~X8K$S)DQ`k~qg@Wal`%_r&Nzb9%*Qg??6UG1Zwo)CA3~kc zg2pl0o_SQZuMbb`T_U0*?Co>aQ&kHWJGPJZ^!--0@1xCGOJ(E!@YSqOguJq!J~SuF zhFVJW_ccxe#DHao1u`}`x}f)p`{n^9;40X0-FTlt&dEV10v82PQR&N-KG7!;epKdv zAPNwVPE4c!^{0iLpfd$0ry{1cRWvOi?-ah$&ayC|K=>v*$YAMpLWUSUC(azpOsyDr zW%a2QWg%Lue2P2p0^O~JH^nqq%pv|-V1UGd@M)k3F6+wn4~Td>KoD_?xIvr-Bs+ct zJ}e}ioINScQH^w3f-Z4-9L$~;U4*}8_sNg0myJ+X{+f(*RF%nToC+h6%d!)&Q%9*d z$L|EEh=ETD_|!`SI>RT{+=0*Vj-383dAcTiBzLz+slX-=(X`t&tecWSwY@g{Sl*+Z z>Rom{eKu$tCOaqpF&{P@yT}_3WTH>=`zJe-EcW4m!NQRkynp&}p#aP-EQm*y2H_c; zhq|n!IFcD7LgN<)Z3t{*Fy;@Z$GG|6pT|y*oy3_g4;}&_G7Ea8J4-FpUa=)%Ve}$$F8!Rhdr?ta|Wmdk<{UF~*M*#W1B2?m6 z_~R7B7)E~aIwcOzWRg34MA{dYa;M(P4_dt)Iz+DwAGYw&A-gxAnG(y{gQ=`gq9bD14+82GTp>4TpqKV-^E$N(zSzJspEZ zPQ9>!35cOi{jHSVDeNWQqfZL|2&kf#pI$H^GYcM&5y*!at5&7%wmG3eQtzLN@HBZ^qsy zFYThAkDHs~1#5Wt4S%o(uBt96;t z8SYvnYrrx9RB;aAqF&<%bptBcEoj>K9B>T|x6a}k9qyOovmtxc;m(cE6q2rvABehx zPF?PV0JtWHn_+Ry4!3>+w{CH`W=q!Ua9>*7F^5Yi=3Y)Z+=$}7#d_CWv}dCNeER5^ zj;3mCbz=|DPTZnvBlJ_4xjH40AQ|J0pegAYw@6dT@MlDgPjf$IWhJS$-CD< zBvV?i8~?xoB7X7!p)V(9rbls^k1j-ub;1Dgk|s?nbh2Cv!uaI`sjc(GVV_EvbhJ6c zI#o`}ah`;1+@|A`_(>@I#iUv(62nhTt`qWL6UCSKnG7oF!{pAAKIsRW?57vyEj8uj z-f$p1I6?M&Nug|NqPI$J=^ItKe*L<(+|Lgfd^S0XS4u|9SAR*vr{?!P3xF`Da=N76 zajW(Sr>lPI${|`ebuduBY3eF*k_JrckMERe8?Yy5re&f2`)MVq)pb|~!pC&?R(QJs zp=Hy1%L|8T$@G5mqgtw*-p8zh#qj&?Li4*A*xYzaF!fxSp(?n&jwWgJ)pSFi*+rM9 z&n&ud7_}ZF#fOuQv)PXrnxw@e3RFf=-RO~Za%G{~Q5O!~y;{h#yXnV!CW%%Wf3I28 zihCWlf&-7=sf>9xK{dZ>IN%PdqVIF>SqD<7UK-lg9?JPzNiS((G%<()8t9K0Du3}ePJxi-V$3|-@&!%xnt6ducIUaBK8 z8_U&1OXg(6?Pfoav~|t^+Xwnl|=t&H!i|bLDtc3BX&HtJCczB+} zUuQl^=@Gti1GPLpGMVAr+5vu@e03xJ`1oKvJm<|DD%&^FBl8Bx9hJ0c-XdAPoxY!! zE8o~oz2*;;*D7i3{C87N?LeP{a6ZG&uW-3_2PH39$tz#J;2~c5_62#eriqM&n`A>B z)h+bPy>;~N!YRRvn50TEaCQqd7e^@KkFVWAJ&sAl3TInF*G+`BznD0Z%H(o()yoJy z_FF6ql2dFO%({4(mMuz~(s4tC5D<9s=JeGX=ZY;tKeCixgS+dNYZ<{D_`8WkR%fx^ z7SJ%$wv&Fl=nr>nWJluL&75LDVCkBkG zAb!{Bf#tJHhPq~-ECH!PQ;>l(i&+5R`4QM+Rhd0xKzD^yN0=T7=F4m0@U~!@kpFR* zUS9lEYMtedrs!@z3F~TCT{vgSb|Eiqqn0Ok%C?H|q@~Xagu?qz^-8k5KkDQ3ho^?; zz9odo+@(Hxk(MqmPHkp)OgAlj-dKh5scI^FZW1LwJtqw~%h%i7 zpwTS+j2tMn(uSwW7eykW08?*2)0f6PGq-bX`0Zx`BK6c3beRG}&7itmj`H~})Lyza zwPM?i`l_ucH*TZSXV;{D%VkI3s_)oJa>W!`K{S0ufvm5gvK2XUV-3B&;@OmD?qrbN z`FSp%C9$GMh@5RS?OnXcGw)-q=-3VLNOOd4pbbJqkK}Z*OO^o{HoMF2>B_mjM^%yh zII39PjXbqJ_Ud|!pb@7U%juJqFW*>R40mIBYvk!#>bYt-eZ1d7Mq9rK|kW$AS7)x6)bUfvS^ zVbgknY_jylZHebCpXKBf-FeG%Wn*1i5aaITwL@9vtWd669A=>=YdGrSw+RBn)0hlc;D zGFPOY2Ml_vJ;;x*|A5~){OG`rW1&W2Xl^up4b%(T3>pvG3Yq}=fm8n}s0VyR|E$BG zb7F@u*ce~MVwkD-Lun&|6P6OqJ{XkuoB8loOd0K}3)mKGYnaj{7 zp?Zj}X@fxp{*bek@@cs+X&?UhZPEwyN=k>@&?Z3o9DW)2b+~C|&CZkqafVh5HV?m^ z_#KF&-nH+GarC>|6QT$0*)={3WQfd1E5bowshfqbNo9StTf{VbG`$Ov(}33yM)(I%W*ZKrh0ne6jP7NS@Z!H$0I_K18|(ic;;ecB diff --git a/contracts/wasm/fairauction/ts/fairauction/params.ts b/contracts/wasm/fairauction/ts/fairauction/params.ts index eccc9a4cb5..ee3ad774c6 100644 --- a/contracts/wasm/fairauction/ts/fairauction/params.ts +++ b/contracts/wasm/fairauction/ts/fairauction/params.ts @@ -10,84 +10,84 @@ import * as sc from "./index"; export class ImmutableFinalizeAuctionParams extends wasmlib.ScMapID { color(): wasmlib.ScImmutableColor { - return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamColor)); } } export class MutableFinalizeAuctionParams extends wasmlib.ScMapID { color(): wasmlib.ScMutableColor { - return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamColor)); } } export class ImmutablePlaceBidParams extends wasmlib.ScMapID { color(): wasmlib.ScImmutableColor { - return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamColor)); } } export class MutablePlaceBidParams extends wasmlib.ScMapID { color(): wasmlib.ScMutableColor { - return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamColor)); } } export class ImmutableSetOwnerMarginParams extends wasmlib.ScMapID { ownerMargin(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamOwnerMargin]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamOwnerMargin)); } } export class MutableSetOwnerMarginParams extends wasmlib.ScMapID { ownerMargin(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamOwnerMargin]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamOwnerMargin)); } } export class ImmutableStartAuctionParams extends wasmlib.ScMapID { color(): wasmlib.ScImmutableColor { - return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamColor)); } description(): wasmlib.ScImmutableString { - return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxParamDescription]); + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamDescription)); } duration(): wasmlib.ScImmutableInt32 { - return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxParamDuration]); + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamDuration)); } minimumBid(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxParamMinimumBid]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamMinimumBid)); } } export class MutableStartAuctionParams extends wasmlib.ScMapID { color(): wasmlib.ScMutableColor { - return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamColor)); } description(): wasmlib.ScMutableString { - return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxParamDescription]); + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamDescription)); } duration(): wasmlib.ScMutableInt32 { - return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxParamDuration]); + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamDuration)); } minimumBid(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxParamMinimumBid]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ParamMinimumBid)); } } export class ImmutableGetInfoParams extends wasmlib.ScMapID { color(): wasmlib.ScImmutableColor { - return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamColor)); } } export class MutableGetInfoParams extends wasmlib.ScMapID { color(): wasmlib.ScMutableColor { - return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxParamColor]); + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ParamColor)); } } diff --git a/contracts/wasm/fairauction/ts/fairauction/results.ts b/contracts/wasm/fairauction/ts/fairauction/results.ts index 3e55c1bb0e..659f739093 100644 --- a/contracts/wasm/fairauction/ts/fairauction/results.ts +++ b/contracts/wasm/fairauction/ts/fairauction/results.ts @@ -10,100 +10,100 @@ import * as sc from "./index"; export class ImmutableGetInfoResults extends wasmlib.ScMapID { bidders(): wasmlib.ScImmutableInt32 { - return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxResultBidders]); + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultBidders)); } color(): wasmlib.ScImmutableColor { - return new wasmlib.ScImmutableColor(this.mapID, sc.idxMap[sc.IdxResultColor]); + return new wasmlib.ScImmutableColor(this.mapID, wasmlib.Key32.fromString(sc.ResultColor)); } creator(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultCreator]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultCreator)); } deposit(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultDeposit]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultDeposit)); } description(): wasmlib.ScImmutableString { - return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultDescription]); + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultDescription)); } duration(): wasmlib.ScImmutableInt32 { - return new wasmlib.ScImmutableInt32(this.mapID, sc.idxMap[sc.IdxResultDuration]); + return new wasmlib.ScImmutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultDuration)); } highestBid(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultHighestBid]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultHighestBid)); } highestBidder(): wasmlib.ScImmutableAgentID { - return new wasmlib.ScImmutableAgentID(this.mapID, sc.idxMap[sc.IdxResultHighestBidder]); + return new wasmlib.ScImmutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultHighestBidder)); } minimumBid(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultMinimumBid]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultMinimumBid)); } numTokens(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultNumTokens]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultNumTokens)); } ownerMargin(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultOwnerMargin]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultOwnerMargin)); } whenStarted(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxResultWhenStarted]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultWhenStarted)); } } export class MutableGetInfoResults extends wasmlib.ScMapID { bidders(): wasmlib.ScMutableInt32 { - return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxResultBidders]); + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultBidders)); } color(): wasmlib.ScMutableColor { - return new wasmlib.ScMutableColor(this.mapID, sc.idxMap[sc.IdxResultColor]); + return new wasmlib.ScMutableColor(this.mapID, wasmlib.Key32.fromString(sc.ResultColor)); } creator(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultCreator]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultCreator)); } deposit(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultDeposit]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultDeposit)); } description(): wasmlib.ScMutableString { - return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultDescription]); + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultDescription)); } duration(): wasmlib.ScMutableInt32 { - return new wasmlib.ScMutableInt32(this.mapID, sc.idxMap[sc.IdxResultDuration]); + return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ResultDuration)); } highestBid(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultHighestBid]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultHighestBid)); } highestBidder(): wasmlib.ScMutableAgentID { - return new wasmlib.ScMutableAgentID(this.mapID, sc.idxMap[sc.IdxResultHighestBidder]); + return new wasmlib.ScMutableAgentID(this.mapID, wasmlib.Key32.fromString(sc.ResultHighestBidder)); } minimumBid(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultMinimumBid]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultMinimumBid)); } numTokens(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultNumTokens]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultNumTokens)); } ownerMargin(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultOwnerMargin]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultOwnerMargin)); } whenStarted(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxResultWhenStarted]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultWhenStarted)); } } diff --git a/contracts/wasm/fairauction/ts/fairauction/state.ts b/contracts/wasm/fairauction/ts/fairauction/state.ts index d9830861c1..5c87258728 100644 --- a/contracts/wasm/fairauction/ts/fairauction/state.ts +++ b/contracts/wasm/fairauction/ts/fairauction/state.ts @@ -48,22 +48,22 @@ export class MapColorToImmutableBids { export class ImmutableFairAuctionState extends wasmlib.ScMapID { auctions(): sc.MapColorToImmutableAuction { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateAuctions], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateAuctions), wasmlib.TYPE_MAP); return new sc.MapColorToImmutableAuction(mapID); } bidderList(): sc.MapColorToImmutableBidderList { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBidderList], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateBidderList), wasmlib.TYPE_MAP); return new sc.MapColorToImmutableBidderList(mapID); } bids(): sc.MapColorToImmutableBids { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBids], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateBids), wasmlib.TYPE_MAP); return new sc.MapColorToImmutableBids(mapID); } ownerMargin(): wasmlib.ScImmutableInt64 { - return new wasmlib.ScImmutableInt64(this.mapID, sc.idxMap[sc.IdxStateOwnerMargin]); + return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.StateOwnerMargin)); } } @@ -125,21 +125,21 @@ export class MutableFairAuctionState extends wasmlib.ScMapID { } auctions(): sc.MapColorToMutableAuction { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateAuctions], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateAuctions), wasmlib.TYPE_MAP); return new sc.MapColorToMutableAuction(mapID); } bidderList(): sc.MapColorToMutableBidderList { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBidderList], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateBidderList), wasmlib.TYPE_MAP); return new sc.MapColorToMutableBidderList(mapID); } bids(): sc.MapColorToMutableBids { - let mapID = wasmlib.getObjectID(this.mapID, sc.idxMap[sc.IdxStateBids], wasmlib.TYPE_MAP); + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateBids), wasmlib.TYPE_MAP); return new sc.MapColorToMutableBids(mapID); } ownerMargin(): wasmlib.ScMutableInt64 { - return new wasmlib.ScMutableInt64(this.mapID, sc.idxMap[sc.IdxStateOwnerMargin]); + return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.StateOwnerMargin)); } } diff --git a/contracts/wasm/fairroulette/go/fairroulette/params.go b/contracts/wasm/fairroulette/go/fairroulette/params.go index f049718b87..7a02cc2470 100644 --- a/contracts/wasm/fairroulette/go/fairroulette/params.go +++ b/contracts/wasm/fairroulette/go/fairroulette/params.go @@ -14,7 +14,7 @@ type ImmutablePlaceBetParams struct { } func (s ImmutablePlaceBetParams) Number() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxParamNumber]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ParamNumber)) } type MutablePlaceBetParams struct { @@ -22,7 +22,7 @@ type MutablePlaceBetParams struct { } func (s MutablePlaceBetParams) Number() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxParamNumber]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ParamNumber)) } type ImmutablePlayPeriodParams struct { @@ -30,7 +30,7 @@ type ImmutablePlayPeriodParams struct { } func (s ImmutablePlayPeriodParams) PlayPeriod() wasmlib.ScImmutableInt32 { - return wasmlib.NewScImmutableInt32(s.id, idxMap[IdxParamPlayPeriod]) + return wasmlib.NewScImmutableInt32(s.id, wasmlib.KeyID(ParamPlayPeriod)) } type MutablePlayPeriodParams struct { @@ -38,5 +38,5 @@ type MutablePlayPeriodParams struct { } func (s MutablePlayPeriodParams) PlayPeriod() wasmlib.ScMutableInt32 { - return wasmlib.NewScMutableInt32(s.id, idxMap[IdxParamPlayPeriod]) + return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(ParamPlayPeriod)) } diff --git a/contracts/wasm/fairroulette/go/fairroulette/results.go b/contracts/wasm/fairroulette/go/fairroulette/results.go index 284847a184..8f4946533c 100644 --- a/contracts/wasm/fairroulette/go/fairroulette/results.go +++ b/contracts/wasm/fairroulette/go/fairroulette/results.go @@ -14,7 +14,7 @@ type ImmutableLastWinningNumberResults struct { } func (s ImmutableLastWinningNumberResults) LastWinningNumber() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultLastWinningNumber]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultLastWinningNumber)) } type MutableLastWinningNumberResults struct { @@ -22,7 +22,7 @@ type MutableLastWinningNumberResults struct { } func (s MutableLastWinningNumberResults) LastWinningNumber() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultLastWinningNumber]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultLastWinningNumber)) } type ImmutableRoundNumberResults struct { @@ -30,7 +30,7 @@ type ImmutableRoundNumberResults struct { } func (s ImmutableRoundNumberResults) RoundNumber() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultRoundNumber]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultRoundNumber)) } type MutableRoundNumberResults struct { @@ -38,7 +38,7 @@ type MutableRoundNumberResults struct { } func (s MutableRoundNumberResults) RoundNumber() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultRoundNumber]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultRoundNumber)) } type ImmutableRoundStartedAtResults struct { @@ -46,7 +46,7 @@ type ImmutableRoundStartedAtResults struct { } func (s ImmutableRoundStartedAtResults) RoundStartedAt() wasmlib.ScImmutableInt32 { - return wasmlib.NewScImmutableInt32(s.id, idxMap[IdxResultRoundStartedAt]) + return wasmlib.NewScImmutableInt32(s.id, wasmlib.KeyID(ResultRoundStartedAt)) } type MutableRoundStartedAtResults struct { @@ -54,7 +54,7 @@ type MutableRoundStartedAtResults struct { } func (s MutableRoundStartedAtResults) RoundStartedAt() wasmlib.ScMutableInt32 { - return wasmlib.NewScMutableInt32(s.id, idxMap[IdxResultRoundStartedAt]) + return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(ResultRoundStartedAt)) } type ImmutableRoundStatusResults struct { @@ -62,7 +62,7 @@ type ImmutableRoundStatusResults struct { } func (s ImmutableRoundStatusResults) RoundStatus() wasmlib.ScImmutableInt16 { - return wasmlib.NewScImmutableInt16(s.id, idxMap[IdxResultRoundStatus]) + return wasmlib.NewScImmutableInt16(s.id, wasmlib.KeyID(ResultRoundStatus)) } type MutableRoundStatusResults struct { @@ -70,5 +70,5 @@ type MutableRoundStatusResults struct { } func (s MutableRoundStatusResults) RoundStatus() wasmlib.ScMutableInt16 { - return wasmlib.NewScMutableInt16(s.id, idxMap[IdxResultRoundStatus]) + return wasmlib.NewScMutableInt16(s.id, wasmlib.KeyID(ResultRoundStatus)) } diff --git a/contracts/wasm/fairroulette/go/fairroulette/state.go b/contracts/wasm/fairroulette/go/fairroulette/state.go index 06c34952c0..e7ce804afe 100644 --- a/contracts/wasm/fairroulette/go/fairroulette/state.go +++ b/contracts/wasm/fairroulette/go/fairroulette/state.go @@ -26,28 +26,28 @@ type ImmutableFairRouletteState struct { } func (s ImmutableFairRouletteState) Bets() ArrayOfImmutableBet { - arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) + arrID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateBets), wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) return ArrayOfImmutableBet{objID: arrID} } func (s ImmutableFairRouletteState) LastWinningNumber() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxStateLastWinningNumber]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(StateLastWinningNumber)) } func (s ImmutableFairRouletteState) PlayPeriod() wasmlib.ScImmutableInt32 { - return wasmlib.NewScImmutableInt32(s.id, idxMap[IdxStatePlayPeriod]) + return wasmlib.NewScImmutableInt32(s.id, wasmlib.KeyID(StatePlayPeriod)) } func (s ImmutableFairRouletteState) RoundNumber() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxStateRoundNumber]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(StateRoundNumber)) } func (s ImmutableFairRouletteState) RoundStartedAt() wasmlib.ScImmutableInt32 { - return wasmlib.NewScImmutableInt32(s.id, idxMap[IdxStateRoundStartedAt]) + return wasmlib.NewScImmutableInt32(s.id, wasmlib.KeyID(StateRoundStartedAt)) } func (s ImmutableFairRouletteState) RoundStatus() wasmlib.ScImmutableInt16 { - return wasmlib.NewScImmutableInt16(s.id, idxMap[IdxStateRoundStatus]) + return wasmlib.NewScImmutableInt16(s.id, wasmlib.KeyID(StateRoundStatus)) } type ArrayOfMutableBet struct { @@ -75,26 +75,26 @@ func (s MutableFairRouletteState) AsImmutable() ImmutableFairRouletteState { } func (s MutableFairRouletteState) Bets() ArrayOfMutableBet { - arrID := wasmlib.GetObjectID(s.id, idxMap[IdxStateBets], wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) + arrID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateBets), wasmlib.TYPE_ARRAY|wasmlib.TYPE_BYTES) return ArrayOfMutableBet{objID: arrID} } func (s MutableFairRouletteState) LastWinningNumber() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateLastWinningNumber]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(StateLastWinningNumber)) } func (s MutableFairRouletteState) PlayPeriod() wasmlib.ScMutableInt32 { - return wasmlib.NewScMutableInt32(s.id, idxMap[IdxStatePlayPeriod]) + return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(StatePlayPeriod)) } func (s MutableFairRouletteState) RoundNumber() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateRoundNumber]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(StateRoundNumber)) } func (s MutableFairRouletteState) RoundStartedAt() wasmlib.ScMutableInt32 { - return wasmlib.NewScMutableInt32(s.id, idxMap[IdxStateRoundStartedAt]) + return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(StateRoundStartedAt)) } func (s MutableFairRouletteState) RoundStatus() wasmlib.ScMutableInt16 { - return wasmlib.NewScMutableInt16(s.id, idxMap[IdxStateRoundStatus]) + return wasmlib.NewScMutableInt16(s.id, wasmlib.KeyID(StateRoundStatus)) } diff --git a/contracts/wasm/fairroulette/src/params.rs b/contracts/wasm/fairroulette/src/params.rs index 34de8c2ed4..cfb876a057 100644 --- a/contracts/wasm/fairroulette/src/params.rs +++ b/contracts/wasm/fairroulette/src/params.rs @@ -22,7 +22,7 @@ pub struct ImmutablePlaceBetParams { impl ImmutablePlaceBetParams { pub fn number(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_NUMBER)) + ScImmutableInt64::new(self.id, PARAM_NUMBER.get_key_id()) } } @@ -33,7 +33,7 @@ pub struct MutablePlaceBetParams { impl MutablePlaceBetParams { pub fn number(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_NUMBER)) + ScMutableInt64::new(self.id, PARAM_NUMBER.get_key_id()) } } @@ -44,7 +44,7 @@ pub struct ImmutablePlayPeriodParams { impl ImmutablePlayPeriodParams { pub fn play_period(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_PLAY_PERIOD)) + ScImmutableInt32::new(self.id, PARAM_PLAY_PERIOD.get_key_id()) } } @@ -55,6 +55,6 @@ pub struct MutablePlayPeriodParams { impl MutablePlayPeriodParams { pub fn play_period(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_PLAY_PERIOD)) + ScMutableInt32::new(self.id, PARAM_PLAY_PERIOD.get_key_id()) } } diff --git a/contracts/wasm/fairroulette/src/results.rs b/contracts/wasm/fairroulette/src/results.rs index 2b6d0b4360..3786d0f879 100644 --- a/contracts/wasm/fairroulette/src/results.rs +++ b/contracts/wasm/fairroulette/src/results.rs @@ -22,7 +22,7 @@ pub struct ImmutableLastWinningNumberResults { impl ImmutableLastWinningNumberResults { pub fn last_winning_number(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_LAST_WINNING_NUMBER)) + ScImmutableInt64::new(self.id, RESULT_LAST_WINNING_NUMBER.get_key_id()) } } @@ -33,7 +33,7 @@ pub struct MutableLastWinningNumberResults { impl MutableLastWinningNumberResults { pub fn last_winning_number(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_LAST_WINNING_NUMBER)) + ScMutableInt64::new(self.id, RESULT_LAST_WINNING_NUMBER.get_key_id()) } } @@ -44,7 +44,7 @@ pub struct ImmutableRoundNumberResults { impl ImmutableRoundNumberResults { pub fn round_number(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_ROUND_NUMBER)) + ScImmutableInt64::new(self.id, RESULT_ROUND_NUMBER.get_key_id()) } } @@ -55,7 +55,7 @@ pub struct MutableRoundNumberResults { impl MutableRoundNumberResults { pub fn round_number(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_ROUND_NUMBER)) + ScMutableInt64::new(self.id, RESULT_ROUND_NUMBER.get_key_id()) } } @@ -66,7 +66,7 @@ pub struct ImmutableRoundStartedAtResults { impl ImmutableRoundStartedAtResults { pub fn round_started_at(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_RESULT_ROUND_STARTED_AT)) + ScImmutableInt32::new(self.id, RESULT_ROUND_STARTED_AT.get_key_id()) } } @@ -77,7 +77,7 @@ pub struct MutableRoundStartedAtResults { impl MutableRoundStartedAtResults { pub fn round_started_at(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_RESULT_ROUND_STARTED_AT)) + ScMutableInt32::new(self.id, RESULT_ROUND_STARTED_AT.get_key_id()) } } @@ -88,7 +88,7 @@ pub struct ImmutableRoundStatusResults { impl ImmutableRoundStatusResults { pub fn round_status(&self) -> ScImmutableInt16 { - ScImmutableInt16::new(self.id, idx_map(IDX_RESULT_ROUND_STATUS)) + ScImmutableInt16::new(self.id, RESULT_ROUND_STATUS.get_key_id()) } } @@ -99,6 +99,6 @@ pub struct MutableRoundStatusResults { impl MutableRoundStatusResults { pub fn round_status(&self) -> ScMutableInt16 { - ScMutableInt16::new(self.id, idx_map(IDX_RESULT_ROUND_STATUS)) + ScMutableInt16::new(self.id, RESULT_ROUND_STATUS.get_key_id()) } } diff --git a/contracts/wasm/fairroulette/src/state.rs b/contracts/wasm/fairroulette/src/state.rs index 72bb5ddf5a..1eab6abc50 100644 --- a/contracts/wasm/fairroulette/src/state.rs +++ b/contracts/wasm/fairroulette/src/state.rs @@ -37,28 +37,28 @@ pub struct ImmutableFairRouletteState { impl ImmutableFairRouletteState { pub fn bets(&self) -> ArrayOfImmutableBet { - let arr_id = get_object_id(self.id, idx_map(IDX_STATE_BETS), TYPE_ARRAY | TYPE_BYTES); + let arr_id = get_object_id(self.id, STATE_BETS.get_key_id(), TYPE_ARRAY | TYPE_BYTES); ArrayOfImmutableBet { obj_id: arr_id } } pub fn last_winning_number(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_LAST_WINNING_NUMBER)) + ScImmutableInt64::new(self.id, STATE_LAST_WINNING_NUMBER.get_key_id()) } pub fn play_period(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_STATE_PLAY_PERIOD)) + ScImmutableInt32::new(self.id, STATE_PLAY_PERIOD.get_key_id()) } pub fn round_number(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_ROUND_NUMBER)) + ScImmutableInt64::new(self.id, STATE_ROUND_NUMBER.get_key_id()) } pub fn round_started_at(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_STATE_ROUND_STARTED_AT)) + ScImmutableInt32::new(self.id, STATE_ROUND_STARTED_AT.get_key_id()) } pub fn round_status(&self) -> ScImmutableInt16 { - ScImmutableInt16::new(self.id, idx_map(IDX_STATE_ROUND_STATUS)) + ScImmutableInt16::new(self.id, STATE_ROUND_STATUS.get_key_id()) } } @@ -92,27 +92,27 @@ impl MutableFairRouletteState { } pub fn bets(&self) -> ArrayOfMutableBet { - let arr_id = get_object_id(self.id, idx_map(IDX_STATE_BETS), TYPE_ARRAY | TYPE_BYTES); + let arr_id = get_object_id(self.id, STATE_BETS.get_key_id(), TYPE_ARRAY | TYPE_BYTES); ArrayOfMutableBet { obj_id: arr_id } } pub fn last_winning_number(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_LAST_WINNING_NUMBER)) + ScMutableInt64::new(self.id, STATE_LAST_WINNING_NUMBER.get_key_id()) } pub fn play_period(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_STATE_PLAY_PERIOD)) + ScMutableInt32::new(self.id, STATE_PLAY_PERIOD.get_key_id()) } pub fn round_number(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_ROUND_NUMBER)) + ScMutableInt64::new(self.id, STATE_ROUND_NUMBER.get_key_id()) } pub fn round_started_at(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_STATE_ROUND_STARTED_AT)) + ScMutableInt32::new(self.id, STATE_ROUND_STARTED_AT.get_key_id()) } pub fn round_status(&self) -> ScMutableInt16 { - ScMutableInt16::new(self.id, idx_map(IDX_STATE_ROUND_STATUS)) + ScMutableInt16::new(self.id, STATE_ROUND_STATUS.get_key_id()) } } diff --git a/contracts/wasm/fairroulette/test/fairroulette_bg.wasm b/contracts/wasm/fairroulette/test/fairroulette_bg.wasm index 6bb26bbfeddd401ce513a605a88c8769612a3492..d7ee5988644911a4fd65865d2ba3914c7c5cfcbe 100644 GIT binary patch delta 7009 zcma)A4Rlr2u|9jB`;(iSo1Ab1Aqgb=-uw`V{EAQlg&Yuqk`U!bWLZEdRJ=hU8c}N{ z5kZ52l4PO;Ob`WIL!J8{kXAxUEZ& z7*x1wpJ)=l#+2>ifVe63P4RP(yF_yS!XqMY!8;=H%PZmoS}2w1q*t?t&wSZk~hlO|YDDh=E;(UY0Y>Osa@r9V_PPHKMTQ96Vf+l2BMo%Dbw+&f%%h6{W6 zK2N5Xhx0V2JXwoQim*pX3|6knur%64Y-wQ)3)Sq^UrWzE<`gg|o2WXhu|>VT7M<=L zu6Sk212rwlN}{E7qf> zTsOH&%nP;+!sd;HB2+Ln-A6Wb_k2b)ZohM8dO6g)qZ%=jW8D`c

cd(z-XLCwwRCj2 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 delta 6959 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

D3hj$2gsf@s2R@KSD={W#U#c!$vt;JiIzOHn{98 z?tDBawm8=6eEWFNd&yy7!+cu%o>)tF){clZPpfK6{eJb^=Ws>Y#=FAQA}gMFSImfJ z4Hv#@^w)oqn=%^lTxVYQz1ZRVwjDYp*jpg|V3}hQPN|3BC1Xh|*W3 z=Fa5e;8F#gxMD7eq;175Tyol~ao&aST+LWSYgXOL)%fVDC3636%3hrh3V+~SL^D>G z%1c`!8&=O1d7GM{SdlslV0Fq+CBd%R4yCV|t~~XXSV&*|e2iEaaX+0U(YoaR?YX2A zXh86G1CPNr`r1Odsg;V?&I0S2wIjg#=2~6Ijji)v6tcGPbk(1UkW`^*KC~f7>hNJD4KJNvzf^yx#6wlj zd?mk6h>Y6Mt5f5~WwO^rS2u8CaTH5`H*b!(DBWC#T;6&B@fT_*7MCRucyvQFtq95eG0!! z{N_;gR!aK|W-EY=#BVTue@URLTaU|K1L*j+<6fX{w^Ju18Co{5KSQ5fzzLMPW8>fsXEsY}nj5nZjvlRDNuq;0CJyKaiT}pXH#;i* z^ZbTp0$T}~Mmw7ZQgvfdTC+1d9+V({&3<~R@eVN{@4XNX;}t(0L ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_HELLO_WORLD)) + ScImmutableString::new(self.id, RESULT_HELLO_WORLD.get_key_id()) } } @@ -32,6 +32,6 @@ pub struct MutableGetHelloWorldResults { impl MutableGetHelloWorldResults { pub fn hello_world(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_HELLO_WORLD)) + ScMutableString::new(self.id, RESULT_HELLO_WORLD.get_key_id()) } } diff --git a/contracts/wasm/helloworld/test/helloworld_bg.wasm b/contracts/wasm/helloworld/test/helloworld_bg.wasm index b31d3d581c6d937bc64572db10373c6df34c6722..3a50d255a537b4191acf8deb808ddfa413eddb05 100644 GIT binary patch delta 3708 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 delta 3681 zcma)9YiwLc6}~g~?%lQTK4xRD?PR^Xv)6Xwq;_Kbh?BSkPl6N2iR~7ss**^NQX!Rf zp}1JI2wFRd)r8g+X(y-!6)7PFgj$J88$=>e5{V!p#RXSI$bb=+h)@7a$q#;1A}V}m z?%j<;kbO= zc6`JOY*^>*fej1TS1P)ET3roJ_*^l`rhGQ(3`)11$KuRWSM`kjAT+&eZ)R8ZiY?m~ zUo`SZ6H)0VmqiQ)2SByZ_FPxr+ zMKTNa`>u+ao=ZzD=y)XiDY6q1o|pDBlUupvNmtqsB|4=mMv8Vk>hVFiz^6nXmiW{p ze1}u^ly9l3SUVKUjM%S6vMz~*^6x~l1H(Sf;ed1|d@j0F#dZsbSY$ey|6w{|sBcB` zKq%#I&XY^hg@xw4i{O^lgl_@A2m<`Jw~k%c)A0<# z{3kD$9+nhAF5QVzX)AZsjP~LZJ38WPcPs$}3Jj|R7SV?2dxR2JOxBi257ADoQlRcp zK)}z;%up2L+*70kHV`??QB14qOxX1o(@mR*2iQyjti%?^#3;0edKlD}U?^zUmpCvs zLa8pgiy%MjWoAs867)Kl57M&(lUt(s6K>5O#2}+ zDbwiglUcN1CSie1fK!5_lqi6q&xsaP`d^8>7nJnX;KkG3T_G_MGG{aqKqv1M{6A;iZczh58*P2|kEKP+SaeIYI+2>hEV-lA%$15-1Bu%HL()7#$|MI50n|rb=R% z9CyrZRk^Z|<1yrRgfi@|;(?b2{-VLX72Ih0Qo|O|?q)+%W9Y7o;HY#*6A_@}!i;=l zQxmNPS1}nR9f*!2TNm|MjRRUBxJS9ejpI#Fl~pM)mBCk!2ZThV*U=kz2*Mg>p8 z@&|=Z4ltNM$@c3n`>kkS_rG31sRi`5XqTW4YLOodZ4vXpjS=0`@_F`-{%%VV?WLB- z(5`LWfL68c%2Ry-|9zXT{#Exq2uW4*Z0k_wPHhAPVs|lW^5kEwBe{wrWTZ&avvsxC zN7`E0N&Q0G=Q4}9a!Wpp&kOri$I6vN$b zh1Q=6%7&sErf9P3D6VVKZ_{&9oyG@9c)D}Lt|bWF%IlGg!APhJp^02gA3znMDeMHj zs0^5?x*Tq?ooV$m{m0HucA;Fe`A3W`>GGB>?1ui!mbQl99>+l#=71j5$3(s5y{z+H z&Fs7$=-Qb$1u1w$ps5vH&vqSVXY}>18+xF7fN}jycV4gW&g`ioR^;k-h!mrGNw6WT z>r}X-h^IvbN={HJ^A@31l)0nbv#`$Xd7RDZQqS;?gUp_8v6R4l+G@;<`O71W1;QGJ2jmM^uIRpAzj+^HtBy0;hUXzvq1W&AY}SwV9%L=L+>_WgjtlxtfL$1Y%lN~?L@-T%m%^MMQ>|4>LPo3<=Vf3N>D@zYT|z{>m3)qLAU z{mX3!)7LCv4P!?uehK`p>7MQ1Ouxp2)r{Fcm}M2vzotLfeu6dYgF8N5^CTB=*0SdG TPj|duOK5q_K~dGO4jlgv&U3d( diff --git a/contracts/wasm/helloworld/ts/helloworld/results.ts b/contracts/wasm/helloworld/ts/helloworld/results.ts index 8af55ab264..b9337efedf 100644 --- a/contracts/wasm/helloworld/ts/helloworld/results.ts +++ b/contracts/wasm/helloworld/ts/helloworld/results.ts @@ -10,12 +10,12 @@ import * as sc from "./index"; export class ImmutableGetHelloWorldResults extends wasmlib.ScMapID { helloWorld(): wasmlib.ScImmutableString { - return new wasmlib.ScImmutableString(this.mapID, sc.idxMap[sc.IdxResultHelloWorld]); + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultHelloWorld)); } } export class MutableGetHelloWorldResults extends wasmlib.ScMapID { helloWorld(): wasmlib.ScMutableString { - return new wasmlib.ScMutableString(this.mapID, sc.idxMap[sc.IdxResultHelloWorld]); + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultHelloWorld)); } } diff --git a/contracts/wasm/inccounter/go/inccounter/params.go b/contracts/wasm/inccounter/go/inccounter/params.go index e3dc8887dc..c9c340257e 100644 --- a/contracts/wasm/inccounter/go/inccounter/params.go +++ b/contracts/wasm/inccounter/go/inccounter/params.go @@ -14,7 +14,7 @@ type ImmutableIncrementWithDelayParams struct { } func (s ImmutableIncrementWithDelayParams) Delay() wasmlib.ScImmutableInt32 { - return wasmlib.NewScImmutableInt32(s.id, idxMap[IdxParamDelay]) + return wasmlib.NewScImmutableInt32(s.id, wasmlib.KeyID(ParamDelay)) } type MutableIncrementWithDelayParams struct { @@ -22,7 +22,7 @@ type MutableIncrementWithDelayParams struct { } func (s MutableIncrementWithDelayParams) Delay() wasmlib.ScMutableInt32 { - return wasmlib.NewScMutableInt32(s.id, idxMap[IdxParamDelay]) + return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(ParamDelay)) } type ImmutableInitParams struct { @@ -46,7 +46,7 @@ type ImmutableRepeatManyParams struct { } func (s ImmutableRepeatManyParams) NumRepeats() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxParamNumRepeats]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ParamNumRepeats)) } type MutableRepeatManyParams struct { @@ -54,7 +54,7 @@ type MutableRepeatManyParams struct { } func (s MutableRepeatManyParams) NumRepeats() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxParamNumRepeats]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ParamNumRepeats)) } type ImmutableWhenMustIncrementParams struct { @@ -62,7 +62,7 @@ type ImmutableWhenMustIncrementParams struct { } func (s ImmutableWhenMustIncrementParams) Dummy() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxParamDummy]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ParamDummy)) } type MutableWhenMustIncrementParams struct { @@ -70,5 +70,5 @@ type MutableWhenMustIncrementParams struct { } func (s MutableWhenMustIncrementParams) Dummy() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxParamDummy]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ParamDummy)) } diff --git a/contracts/wasm/inccounter/go/inccounter/results.go b/contracts/wasm/inccounter/go/inccounter/results.go index b197174349..585f59b62f 100644 --- a/contracts/wasm/inccounter/go/inccounter/results.go +++ b/contracts/wasm/inccounter/go/inccounter/results.go @@ -14,7 +14,7 @@ type ImmutableGetCounterResults struct { } func (s ImmutableGetCounterResults) Counter() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxResultCounter]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(ResultCounter)) } type MutableGetCounterResults struct { @@ -22,5 +22,5 @@ type MutableGetCounterResults struct { } func (s MutableGetCounterResults) Counter() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxResultCounter]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultCounter)) } diff --git a/contracts/wasm/inccounter/go/inccounter/state.go b/contracts/wasm/inccounter/go/inccounter/state.go index 9d3ddb0b6b..f406babfb8 100644 --- a/contracts/wasm/inccounter/go/inccounter/state.go +++ b/contracts/wasm/inccounter/go/inccounter/state.go @@ -14,11 +14,11 @@ type ImmutableIncCounterState struct { } func (s ImmutableIncCounterState) Counter() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxStateCounter]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(StateCounter)) } func (s ImmutableIncCounterState) NumRepeats() wasmlib.ScImmutableInt64 { - return wasmlib.NewScImmutableInt64(s.id, idxMap[IdxStateNumRepeats]) + return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(StateNumRepeats)) } type MutableIncCounterState struct { @@ -30,9 +30,9 @@ func (s MutableIncCounterState) AsImmutable() ImmutableIncCounterState { } func (s MutableIncCounterState) Counter() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateCounter]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(StateCounter)) } func (s MutableIncCounterState) NumRepeats() wasmlib.ScMutableInt64 { - return wasmlib.NewScMutableInt64(s.id, idxMap[IdxStateNumRepeats]) + return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(StateNumRepeats)) } diff --git a/contracts/wasm/inccounter/src/params.rs b/contracts/wasm/inccounter/src/params.rs index edcb169d0a..27d5a8ff01 100644 --- a/contracts/wasm/inccounter/src/params.rs +++ b/contracts/wasm/inccounter/src/params.rs @@ -21,7 +21,7 @@ pub struct ImmutableIncrementWithDelayParams { impl ImmutableIncrementWithDelayParams { pub fn delay(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_DELAY)) + ScImmutableInt32::new(self.id, PARAM_DELAY.get_key_id()) } } @@ -32,7 +32,7 @@ pub struct MutableIncrementWithDelayParams { impl MutableIncrementWithDelayParams { pub fn delay(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_DELAY)) + ScMutableInt32::new(self.id, PARAM_DELAY.get_key_id()) } } @@ -65,7 +65,7 @@ pub struct ImmutableRepeatManyParams { impl ImmutableRepeatManyParams { pub fn num_repeats(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_NUM_REPEATS)) + ScImmutableInt64::new(self.id, PARAM_NUM_REPEATS.get_key_id()) } } @@ -76,7 +76,7 @@ pub struct MutableRepeatManyParams { impl MutableRepeatManyParams { pub fn num_repeats(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_NUM_REPEATS)) + ScMutableInt64::new(self.id, PARAM_NUM_REPEATS.get_key_id()) } } @@ -87,7 +87,7 @@ pub struct ImmutableWhenMustIncrementParams { impl ImmutableWhenMustIncrementParams { pub fn dummy(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_DUMMY)) + ScImmutableInt64::new(self.id, PARAM_DUMMY.get_key_id()) } } @@ -98,6 +98,6 @@ pub struct MutableWhenMustIncrementParams { impl MutableWhenMustIncrementParams { pub fn dummy(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_DUMMY)) + ScMutableInt64::new(self.id, PARAM_DUMMY.get_key_id()) } } diff --git a/contracts/wasm/inccounter/src/results.rs b/contracts/wasm/inccounter/src/results.rs index 74b1aa5bdd..4106744dcb 100644 --- a/contracts/wasm/inccounter/src/results.rs +++ b/contracts/wasm/inccounter/src/results.rs @@ -21,7 +21,7 @@ pub struct ImmutableGetCounterResults { impl ImmutableGetCounterResults { pub fn counter(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_COUNTER)) + ScImmutableInt64::new(self.id, RESULT_COUNTER.get_key_id()) } } @@ -32,6 +32,6 @@ pub struct MutableGetCounterResults { impl MutableGetCounterResults { pub fn counter(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_COUNTER)) + ScMutableInt64::new(self.id, RESULT_COUNTER.get_key_id()) } } diff --git a/contracts/wasm/inccounter/src/state.rs b/contracts/wasm/inccounter/src/state.rs index c37fa239da..908f5c9aeb 100644 --- a/contracts/wasm/inccounter/src/state.rs +++ b/contracts/wasm/inccounter/src/state.rs @@ -21,11 +21,11 @@ pub struct ImmutableIncCounterState { impl ImmutableIncCounterState { pub fn counter(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_COUNTER)) + ScImmutableInt64::new(self.id, STATE_COUNTER.get_key_id()) } pub fn num_repeats(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_NUM_REPEATS)) + ScImmutableInt64::new(self.id, STATE_NUM_REPEATS.get_key_id()) } } @@ -40,10 +40,10 @@ impl MutableIncCounterState { } pub fn counter(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_COUNTER)) + ScMutableInt64::new(self.id, STATE_COUNTER.get_key_id()) } pub fn num_repeats(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_NUM_REPEATS)) + ScMutableInt64::new(self.id, STATE_NUM_REPEATS.get_key_id()) } } diff --git a/contracts/wasm/inccounter/test/inccounter_bg.wasm b/contracts/wasm/inccounter/test/inccounter_bg.wasm index c577e6e18ff82836a7db21ef397eef2f2517bb33..8f2f783f823869861ca1a1735e9f2f5197584f2e 100644 GIT binary patch delta 6418 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 ScImmutableHname { - ScImmutableHname::new(self.id, idx_map(IDX_PARAM_HNAME_CONTRACT)) + ScImmutableHname::new(self.id, PARAM_HNAME_CONTRACT.get_key_id()) } pub fn hname_ep(&self) -> ScImmutableHname { - ScImmutableHname::new(self.id, idx_map(IDX_PARAM_HNAME_EP)) + ScImmutableHname::new(self.id, PARAM_HNAME_EP.get_key_id()) } pub fn int_value(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT_VALUE)) + ScImmutableInt64::new(self.id, PARAM_INT_VALUE.get_key_id()) } } @@ -40,15 +40,15 @@ pub struct MutableCallOnChainParams { impl MutableCallOnChainParams { pub fn hname_contract(&self) -> ScMutableHname { - ScMutableHname::new(self.id, idx_map(IDX_PARAM_HNAME_CONTRACT)) + ScMutableHname::new(self.id, PARAM_HNAME_CONTRACT.get_key_id()) } pub fn hname_ep(&self) -> ScMutableHname { - ScMutableHname::new(self.id, idx_map(IDX_PARAM_HNAME_EP)) + ScMutableHname::new(self.id, PARAM_HNAME_EP.get_key_id()) } pub fn int_value(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT_VALUE)) + ScMutableInt64::new(self.id, PARAM_INT_VALUE.get_key_id()) } } @@ -59,23 +59,23 @@ pub struct ImmutableCheckContextFromFullEPParams { impl ImmutableCheckContextFromFullEPParams { pub fn agent_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScImmutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn caller(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_CALLER)) + ScImmutableAgentID::new(self.id, PARAM_CALLER.get_key_id()) } pub fn chain_id(&self) -> ScImmutableChainID { - ScImmutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScImmutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn chain_owner_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_CHAIN_OWNER_ID)) + ScImmutableAgentID::new(self.id, PARAM_CHAIN_OWNER_ID.get_key_id()) } pub fn contract_creator(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_CONTRACT_CREATOR)) + ScImmutableAgentID::new(self.id, PARAM_CONTRACT_CREATOR.get_key_id()) } } @@ -86,23 +86,23 @@ pub struct MutableCheckContextFromFullEPParams { impl MutableCheckContextFromFullEPParams { pub fn agent_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScMutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn caller(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_CALLER)) + ScMutableAgentID::new(self.id, PARAM_CALLER.get_key_id()) } pub fn chain_id(&self) -> ScMutableChainID { - ScMutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScMutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn chain_owner_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_CHAIN_OWNER_ID)) + ScMutableAgentID::new(self.id, PARAM_CHAIN_OWNER_ID.get_key_id()) } pub fn contract_creator(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_CONTRACT_CREATOR)) + ScMutableAgentID::new(self.id, PARAM_CONTRACT_CREATOR.get_key_id()) } } @@ -135,47 +135,47 @@ pub struct ImmutablePassTypesFullParams { impl ImmutablePassTypesFullParams { pub fn address(&self) -> ScImmutableAddress { - ScImmutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScImmutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn agent_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScImmutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn chain_id(&self) -> ScImmutableChainID { - ScImmutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScImmutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn contract_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_CONTRACT_ID)) + ScImmutableAgentID::new(self.id, PARAM_CONTRACT_ID.get_key_id()) } pub fn hash(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_HASH)) + ScImmutableHash::new(self.id, PARAM_HASH.get_key_id()) } pub fn hname(&self) -> ScImmutableHname { - ScImmutableHname::new(self.id, idx_map(IDX_PARAM_HNAME)) + ScImmutableHname::new(self.id, PARAM_HNAME.get_key_id()) } pub fn hname_zero(&self) -> ScImmutableHname { - ScImmutableHname::new(self.id, idx_map(IDX_PARAM_HNAME_ZERO)) + ScImmutableHname::new(self.id, PARAM_HNAME_ZERO.get_key_id()) } pub fn int64(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT64)) + ScImmutableInt64::new(self.id, PARAM_INT64.get_key_id()) } pub fn int64_zero(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT64_ZERO)) + ScImmutableInt64::new(self.id, PARAM_INT64_ZERO.get_key_id()) } pub fn string(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_STRING)) + ScImmutableString::new(self.id, PARAM_STRING.get_key_id()) } pub fn string_zero(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_STRING_ZERO)) + ScImmutableString::new(self.id, PARAM_STRING_ZERO.get_key_id()) } } @@ -186,47 +186,47 @@ pub struct MutablePassTypesFullParams { impl MutablePassTypesFullParams { pub fn address(&self) -> ScMutableAddress { - ScMutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScMutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn agent_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScMutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn chain_id(&self) -> ScMutableChainID { - ScMutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScMutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn contract_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_CONTRACT_ID)) + ScMutableAgentID::new(self.id, PARAM_CONTRACT_ID.get_key_id()) } pub fn hash(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_HASH)) + ScMutableHash::new(self.id, PARAM_HASH.get_key_id()) } pub fn hname(&self) -> ScMutableHname { - ScMutableHname::new(self.id, idx_map(IDX_PARAM_HNAME)) + ScMutableHname::new(self.id, PARAM_HNAME.get_key_id()) } pub fn hname_zero(&self) -> ScMutableHname { - ScMutableHname::new(self.id, idx_map(IDX_PARAM_HNAME_ZERO)) + ScMutableHname::new(self.id, PARAM_HNAME_ZERO.get_key_id()) } pub fn int64(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT64)) + ScMutableInt64::new(self.id, PARAM_INT64.get_key_id()) } pub fn int64_zero(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT64_ZERO)) + ScMutableInt64::new(self.id, PARAM_INT64_ZERO.get_key_id()) } pub fn string(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_STRING)) + ScMutableString::new(self.id, PARAM_STRING.get_key_id()) } pub fn string_zero(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_STRING_ZERO)) + ScMutableString::new(self.id, PARAM_STRING_ZERO.get_key_id()) } } @@ -237,7 +237,7 @@ pub struct ImmutableRunRecursionParams { impl ImmutableRunRecursionParams { pub fn int_value(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT_VALUE)) + ScImmutableInt64::new(self.id, PARAM_INT_VALUE.get_key_id()) } } @@ -248,7 +248,7 @@ pub struct MutableRunRecursionParams { impl MutableRunRecursionParams { pub fn int_value(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT_VALUE)) + ScMutableInt64::new(self.id, PARAM_INT_VALUE.get_key_id()) } } @@ -259,7 +259,7 @@ pub struct ImmutableSendToAddressParams { impl ImmutableSendToAddressParams { pub fn address(&self) -> ScImmutableAddress { - ScImmutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScImmutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } } @@ -270,7 +270,7 @@ pub struct MutableSendToAddressParams { impl MutableSendToAddressParams { pub fn address(&self) -> ScMutableAddress { - ScMutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScMutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } } @@ -281,11 +281,11 @@ pub struct ImmutableSetIntParams { impl ImmutableSetIntParams { pub fn int_value(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT_VALUE)) + ScImmutableInt64::new(self.id, PARAM_INT_VALUE.get_key_id()) } pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -296,11 +296,11 @@ pub struct MutableSetIntParams { impl MutableSetIntParams { pub fn int_value(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT_VALUE)) + ScMutableInt64::new(self.id, PARAM_INT_VALUE.get_key_id()) } pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -311,7 +311,7 @@ pub struct ImmutableSpawnParams { impl ImmutableSpawnParams { pub fn prog_hash(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_PROG_HASH)) + ScImmutableHash::new(self.id, PARAM_PROG_HASH.get_key_id()) } } @@ -322,7 +322,7 @@ pub struct MutableSpawnParams { impl MutableSpawnParams { pub fn prog_hash(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_PROG_HASH)) + ScMutableHash::new(self.id, PARAM_PROG_HASH.get_key_id()) } } @@ -333,7 +333,7 @@ pub struct ImmutableTestEventLogGenericDataParams { impl ImmutableTestEventLogGenericDataParams { pub fn counter(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_COUNTER)) + ScImmutableInt64::new(self.id, PARAM_COUNTER.get_key_id()) } } @@ -344,7 +344,7 @@ pub struct MutableTestEventLogGenericDataParams { impl MutableTestEventLogGenericDataParams { pub fn counter(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_COUNTER)) + ScMutableInt64::new(self.id, PARAM_COUNTER.get_key_id()) } } @@ -355,7 +355,7 @@ pub struct ImmutableWithdrawToChainParams { impl ImmutableWithdrawToChainParams { pub fn chain_id(&self) -> ScImmutableChainID { - ScImmutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScImmutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } } @@ -366,7 +366,7 @@ pub struct MutableWithdrawToChainParams { impl MutableWithdrawToChainParams { pub fn chain_id(&self) -> ScMutableChainID { - ScMutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScMutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } } @@ -377,19 +377,19 @@ pub struct ImmutableCheckContextFromViewEPParams { impl ImmutableCheckContextFromViewEPParams { pub fn agent_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScImmutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn chain_id(&self) -> ScImmutableChainID { - ScImmutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScImmutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn chain_owner_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_CHAIN_OWNER_ID)) + ScImmutableAgentID::new(self.id, PARAM_CHAIN_OWNER_ID.get_key_id()) } pub fn contract_creator(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_CONTRACT_CREATOR)) + ScImmutableAgentID::new(self.id, PARAM_CONTRACT_CREATOR.get_key_id()) } } @@ -400,19 +400,19 @@ pub struct MutableCheckContextFromViewEPParams { impl MutableCheckContextFromViewEPParams { pub fn agent_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScMutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn chain_id(&self) -> ScMutableChainID { - ScMutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScMutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn chain_owner_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_CHAIN_OWNER_ID)) + ScMutableAgentID::new(self.id, PARAM_CHAIN_OWNER_ID.get_key_id()) } pub fn contract_creator(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_CONTRACT_CREATOR)) + ScMutableAgentID::new(self.id, PARAM_CONTRACT_CREATOR.get_key_id()) } } @@ -423,7 +423,7 @@ pub struct ImmutableFibonacciParams { impl ImmutableFibonacciParams { pub fn int_value(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT_VALUE)) + ScImmutableInt64::new(self.id, PARAM_INT_VALUE.get_key_id()) } } @@ -434,7 +434,7 @@ pub struct MutableFibonacciParams { impl MutableFibonacciParams { pub fn int_value(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT_VALUE)) + ScMutableInt64::new(self.id, PARAM_INT_VALUE.get_key_id()) } } @@ -445,7 +445,7 @@ pub struct ImmutableGetIntParams { impl ImmutableGetIntParams { pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -456,7 +456,7 @@ pub struct MutableGetIntParams { impl MutableGetIntParams { pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -467,7 +467,7 @@ pub struct ImmutableGetStringValueParams { impl ImmutableGetStringValueParams { pub fn var_name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_VAR_NAME)) + ScImmutableString::new(self.id, PARAM_VAR_NAME.get_key_id()) } } @@ -478,7 +478,7 @@ pub struct MutableGetStringValueParams { impl MutableGetStringValueParams { pub fn var_name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_VAR_NAME)) + ScMutableString::new(self.id, PARAM_VAR_NAME.get_key_id()) } } @@ -489,47 +489,47 @@ pub struct ImmutablePassTypesViewParams { impl ImmutablePassTypesViewParams { pub fn address(&self) -> ScImmutableAddress { - ScImmutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScImmutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn agent_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScImmutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn chain_id(&self) -> ScImmutableChainID { - ScImmutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScImmutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn contract_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_CONTRACT_ID)) + ScImmutableAgentID::new(self.id, PARAM_CONTRACT_ID.get_key_id()) } pub fn hash(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_HASH)) + ScImmutableHash::new(self.id, PARAM_HASH.get_key_id()) } pub fn hname(&self) -> ScImmutableHname { - ScImmutableHname::new(self.id, idx_map(IDX_PARAM_HNAME)) + ScImmutableHname::new(self.id, PARAM_HNAME.get_key_id()) } pub fn hname_zero(&self) -> ScImmutableHname { - ScImmutableHname::new(self.id, idx_map(IDX_PARAM_HNAME_ZERO)) + ScImmutableHname::new(self.id, PARAM_HNAME_ZERO.get_key_id()) } pub fn int64(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT64)) + ScImmutableInt64::new(self.id, PARAM_INT64.get_key_id()) } pub fn int64_zero(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT64_ZERO)) + ScImmutableInt64::new(self.id, PARAM_INT64_ZERO.get_key_id()) } pub fn string(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_STRING)) + ScImmutableString::new(self.id, PARAM_STRING.get_key_id()) } pub fn string_zero(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_STRING_ZERO)) + ScImmutableString::new(self.id, PARAM_STRING_ZERO.get_key_id()) } } @@ -540,46 +540,46 @@ pub struct MutablePassTypesViewParams { impl MutablePassTypesViewParams { pub fn address(&self) -> ScMutableAddress { - ScMutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScMutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn agent_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScMutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn chain_id(&self) -> ScMutableChainID { - ScMutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScMutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn contract_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_CONTRACT_ID)) + ScMutableAgentID::new(self.id, PARAM_CONTRACT_ID.get_key_id()) } pub fn hash(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_HASH)) + ScMutableHash::new(self.id, PARAM_HASH.get_key_id()) } pub fn hname(&self) -> ScMutableHname { - ScMutableHname::new(self.id, idx_map(IDX_PARAM_HNAME)) + ScMutableHname::new(self.id, PARAM_HNAME.get_key_id()) } pub fn hname_zero(&self) -> ScMutableHname { - ScMutableHname::new(self.id, idx_map(IDX_PARAM_HNAME_ZERO)) + ScMutableHname::new(self.id, PARAM_HNAME_ZERO.get_key_id()) } pub fn int64(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT64)) + ScMutableInt64::new(self.id, PARAM_INT64.get_key_id()) } pub fn int64_zero(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT64_ZERO)) + ScMutableInt64::new(self.id, PARAM_INT64_ZERO.get_key_id()) } pub fn string(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_STRING)) + ScMutableString::new(self.id, PARAM_STRING.get_key_id()) } pub fn string_zero(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_STRING_ZERO)) + ScMutableString::new(self.id, PARAM_STRING_ZERO.get_key_id()) } } diff --git a/contracts/wasm/testcore/src/results.rs b/contracts/wasm/testcore/src/results.rs index 0971eb7005..a0380fbbfa 100644 --- a/contracts/wasm/testcore/src/results.rs +++ b/contracts/wasm/testcore/src/results.rs @@ -21,7 +21,7 @@ pub struct ImmutableCallOnChainResults { impl ImmutableCallOnChainResults { pub fn int_value(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_INT_VALUE)) + ScImmutableInt64::new(self.id, RESULT_INT_VALUE.get_key_id()) } } @@ -32,7 +32,7 @@ pub struct MutableCallOnChainResults { impl MutableCallOnChainResults { pub fn int_value(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_INT_VALUE)) + ScMutableInt64::new(self.id, RESULT_INT_VALUE.get_key_id()) } } @@ -43,11 +43,11 @@ pub struct ImmutableGetMintedSupplyResults { impl ImmutableGetMintedSupplyResults { pub fn minted_color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_RESULT_MINTED_COLOR)) + ScImmutableColor::new(self.id, RESULT_MINTED_COLOR.get_key_id()) } pub fn minted_supply(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_MINTED_SUPPLY)) + ScImmutableInt64::new(self.id, RESULT_MINTED_SUPPLY.get_key_id()) } } @@ -58,11 +58,11 @@ pub struct MutableGetMintedSupplyResults { impl MutableGetMintedSupplyResults { pub fn minted_color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_RESULT_MINTED_COLOR)) + ScMutableColor::new(self.id, RESULT_MINTED_COLOR.get_key_id()) } pub fn minted_supply(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_MINTED_SUPPLY)) + ScMutableInt64::new(self.id, RESULT_MINTED_SUPPLY.get_key_id()) } } @@ -73,7 +73,7 @@ pub struct ImmutableRunRecursionResults { impl ImmutableRunRecursionResults { pub fn int_value(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_INT_VALUE)) + ScImmutableInt64::new(self.id, RESULT_INT_VALUE.get_key_id()) } } @@ -84,7 +84,7 @@ pub struct MutableRunRecursionResults { impl MutableRunRecursionResults { pub fn int_value(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_INT_VALUE)) + ScMutableInt64::new(self.id, RESULT_INT_VALUE.get_key_id()) } } @@ -95,7 +95,7 @@ pub struct ImmutableTestChainOwnerIDFullResults { impl ImmutableTestChainOwnerIDFullResults { pub fn chain_owner_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_RESULT_CHAIN_OWNER_ID)) + ScImmutableAgentID::new(self.id, RESULT_CHAIN_OWNER_ID.get_key_id()) } } @@ -106,7 +106,7 @@ pub struct MutableTestChainOwnerIDFullResults { impl MutableTestChainOwnerIDFullResults { pub fn chain_owner_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_RESULT_CHAIN_OWNER_ID)) + ScMutableAgentID::new(self.id, RESULT_CHAIN_OWNER_ID.get_key_id()) } } @@ -117,7 +117,7 @@ pub struct ImmutableFibonacciResults { impl ImmutableFibonacciResults { pub fn int_value(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_INT_VALUE)) + ScImmutableInt64::new(self.id, RESULT_INT_VALUE.get_key_id()) } } @@ -128,7 +128,7 @@ pub struct MutableFibonacciResults { impl MutableFibonacciResults { pub fn int_value(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_INT_VALUE)) + ScMutableInt64::new(self.id, RESULT_INT_VALUE.get_key_id()) } } @@ -139,7 +139,7 @@ pub struct ImmutableGetCounterResults { impl ImmutableGetCounterResults { pub fn counter(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_COUNTER)) + ScImmutableInt64::new(self.id, RESULT_COUNTER.get_key_id()) } } @@ -150,7 +150,7 @@ pub struct MutableGetCounterResults { impl MutableGetCounterResults { pub fn counter(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_COUNTER)) + ScMutableInt64::new(self.id, RESULT_COUNTER.get_key_id()) } } @@ -257,7 +257,7 @@ pub struct ImmutableTestChainOwnerIDViewResults { impl ImmutableTestChainOwnerIDViewResults { pub fn chain_owner_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_RESULT_CHAIN_OWNER_ID)) + ScImmutableAgentID::new(self.id, RESULT_CHAIN_OWNER_ID.get_key_id()) } } @@ -268,7 +268,7 @@ pub struct MutableTestChainOwnerIDViewResults { impl MutableTestChainOwnerIDViewResults { pub fn chain_owner_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_RESULT_CHAIN_OWNER_ID)) + ScMutableAgentID::new(self.id, RESULT_CHAIN_OWNER_ID.get_key_id()) } } @@ -279,7 +279,7 @@ pub struct ImmutableTestSandboxCallResults { impl ImmutableTestSandboxCallResults { pub fn sandbox_call(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_SANDBOX_CALL)) + ScImmutableString::new(self.id, RESULT_SANDBOX_CALL.get_key_id()) } } @@ -290,6 +290,6 @@ pub struct MutableTestSandboxCallResults { impl MutableTestSandboxCallResults { pub fn sandbox_call(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_SANDBOX_CALL)) + ScMutableString::new(self.id, RESULT_SANDBOX_CALL.get_key_id()) } } diff --git a/contracts/wasm/testcore/src/state.rs b/contracts/wasm/testcore/src/state.rs index ece8ea4db2..464c4e0aa9 100644 --- a/contracts/wasm/testcore/src/state.rs +++ b/contracts/wasm/testcore/src/state.rs @@ -21,24 +21,24 @@ pub struct ImmutableTestCoreState { impl ImmutableTestCoreState { pub fn counter(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_COUNTER)) + ScImmutableInt64::new(self.id, STATE_COUNTER.get_key_id()) } pub fn hname_ep(&self) -> ScImmutableHname { - ScImmutableHname::new(self.id, idx_map(IDX_STATE_HNAME_EP)) + ScImmutableHname::new(self.id, STATE_HNAME_EP.get_key_id()) } pub fn ints(&self) -> MapStringToImmutableInt64 { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_INTS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_INTS.get_key_id(), TYPE_MAP); MapStringToImmutableInt64 { obj_id: map_id } } pub fn minted_color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_STATE_MINTED_COLOR)) + ScImmutableColor::new(self.id, STATE_MINTED_COLOR.get_key_id()) } pub fn minted_supply(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_MINTED_SUPPLY)) + ScImmutableInt64::new(self.id, STATE_MINTED_SUPPLY.get_key_id()) } } @@ -53,23 +53,23 @@ impl MutableTestCoreState { } pub fn counter(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_COUNTER)) + ScMutableInt64::new(self.id, STATE_COUNTER.get_key_id()) } pub fn hname_ep(&self) -> ScMutableHname { - ScMutableHname::new(self.id, idx_map(IDX_STATE_HNAME_EP)) + ScMutableHname::new(self.id, STATE_HNAME_EP.get_key_id()) } pub fn ints(&self) -> MapStringToMutableInt64 { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_INTS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_INTS.get_key_id(), TYPE_MAP); MapStringToMutableInt64 { obj_id: map_id } } pub fn minted_color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_STATE_MINTED_COLOR)) + ScMutableColor::new(self.id, STATE_MINTED_COLOR.get_key_id()) } pub fn minted_supply(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_MINTED_SUPPLY)) + ScMutableInt64::new(self.id, STATE_MINTED_SUPPLY.get_key_id()) } } diff --git a/contracts/wasm/testcore/test/testcore_bg.wasm b/contracts/wasm/testcore/test/testcore_bg.wasm index 97a155c837bdf3b46df9d975b03c73bedb71db88..4c5b2af5841994a57b929dcc5c3929997b2ad39b 100644 GIT binary patch delta 12613 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 WqmO=UvQd#>!~ZFP-U%h`1k+eORDSW>gww1 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 ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -33,7 +33,7 @@ pub struct MutableArrayClearParams { impl MutableArrayClearParams { pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -44,7 +44,7 @@ pub struct ImmutableArrayCreateParams { impl ImmutableArrayCreateParams { pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -55,7 +55,7 @@ pub struct MutableArrayCreateParams { impl MutableArrayCreateParams { pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -66,15 +66,15 @@ pub struct ImmutableArraySetParams { impl ImmutableArraySetParams { pub fn index(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_INDEX)) + ScImmutableInt32::new(self.id, PARAM_INDEX.get_key_id()) } pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) } pub fn value(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_VALUE)) + ScImmutableString::new(self.id, PARAM_VALUE.get_key_id()) } } @@ -85,15 +85,15 @@ pub struct MutableArraySetParams { impl MutableArraySetParams { pub fn index(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_INDEX)) + ScMutableInt32::new(self.id, PARAM_INDEX.get_key_id()) } pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) } pub fn value(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_VALUE)) + ScMutableString::new(self.id, PARAM_VALUE.get_key_id()) } } @@ -115,51 +115,51 @@ pub struct ImmutableParamTypesParams { impl ImmutableParamTypesParams { pub fn address(&self) -> ScImmutableAddress { - ScImmutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScImmutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn agent_id(&self) -> ScImmutableAgentID { - ScImmutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScImmutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn bool(&self) -> ScImmutableBool { - ScImmutableBool::new(self.id, idx_map(IDX_PARAM_BOOL)) + ScImmutableBool::new(self.id, PARAM_BOOL.get_key_id()) } pub fn bytes(&self) -> ScImmutableBytes { - ScImmutableBytes::new(self.id, idx_map(IDX_PARAM_BYTES)) + ScImmutableBytes::new(self.id, PARAM_BYTES.get_key_id()) } pub fn chain_id(&self) -> ScImmutableChainID { - ScImmutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScImmutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScImmutableColor::new(self.id, PARAM_COLOR.get_key_id()) } pub fn hash(&self) -> ScImmutableHash { - ScImmutableHash::new(self.id, idx_map(IDX_PARAM_HASH)) + ScImmutableHash::new(self.id, PARAM_HASH.get_key_id()) } pub fn hname(&self) -> ScImmutableHname { - ScImmutableHname::new(self.id, idx_map(IDX_PARAM_HNAME)) + ScImmutableHname::new(self.id, PARAM_HNAME.get_key_id()) } pub fn int16(&self) -> ScImmutableInt16 { - ScImmutableInt16::new(self.id, idx_map(IDX_PARAM_INT16)) + ScImmutableInt16::new(self.id, PARAM_INT16.get_key_id()) } pub fn int32(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_INT32)) + ScImmutableInt32::new(self.id, PARAM_INT32.get_key_id()) } pub fn int64(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_PARAM_INT64)) + ScImmutableInt64::new(self.id, PARAM_INT64.get_key_id()) } pub fn int8(&self) -> ScImmutableInt8 { - ScImmutableInt8::new(self.id, idx_map(IDX_PARAM_INT8)) + ScImmutableInt8::new(self.id, PARAM_INT8.get_key_id()) } pub fn param(&self) -> MapStringToImmutableBytes { @@ -167,27 +167,27 @@ impl ImmutableParamTypesParams { } pub fn request_id(&self) -> ScImmutableRequestID { - ScImmutableRequestID::new(self.id, idx_map(IDX_PARAM_REQUEST_ID)) + ScImmutableRequestID::new(self.id, PARAM_REQUEST_ID.get_key_id()) } pub fn string(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_STRING)) + ScImmutableString::new(self.id, PARAM_STRING.get_key_id()) } pub fn uint16(&self) -> ScImmutableUint16 { - ScImmutableUint16::new(self.id, idx_map(IDX_PARAM_UINT16)) + ScImmutableUint16::new(self.id, PARAM_UINT16.get_key_id()) } pub fn uint32(&self) -> ScImmutableUint32 { - ScImmutableUint32::new(self.id, idx_map(IDX_PARAM_UINT32)) + ScImmutableUint32::new(self.id, PARAM_UINT32.get_key_id()) } pub fn uint64(&self) -> ScImmutableUint64 { - ScImmutableUint64::new(self.id, idx_map(IDX_PARAM_UINT64)) + ScImmutableUint64::new(self.id, PARAM_UINT64.get_key_id()) } pub fn uint8(&self) -> ScImmutableUint8 { - ScImmutableUint8::new(self.id, idx_map(IDX_PARAM_UINT8)) + ScImmutableUint8::new(self.id, PARAM_UINT8.get_key_id()) } } @@ -213,51 +213,51 @@ pub struct MutableParamTypesParams { impl MutableParamTypesParams { pub fn address(&self) -> ScMutableAddress { - ScMutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScMutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn agent_id(&self) -> ScMutableAgentID { - ScMutableAgentID::new(self.id, idx_map(IDX_PARAM_AGENT_ID)) + ScMutableAgentID::new(self.id, PARAM_AGENT_ID.get_key_id()) } pub fn bool(&self) -> ScMutableBool { - ScMutableBool::new(self.id, idx_map(IDX_PARAM_BOOL)) + ScMutableBool::new(self.id, PARAM_BOOL.get_key_id()) } pub fn bytes(&self) -> ScMutableBytes { - ScMutableBytes::new(self.id, idx_map(IDX_PARAM_BYTES)) + ScMutableBytes::new(self.id, PARAM_BYTES.get_key_id()) } pub fn chain_id(&self) -> ScMutableChainID { - ScMutableChainID::new(self.id, idx_map(IDX_PARAM_CHAIN_ID)) + ScMutableChainID::new(self.id, PARAM_CHAIN_ID.get_key_id()) } pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScMutableColor::new(self.id, PARAM_COLOR.get_key_id()) } pub fn hash(&self) -> ScMutableHash { - ScMutableHash::new(self.id, idx_map(IDX_PARAM_HASH)) + ScMutableHash::new(self.id, PARAM_HASH.get_key_id()) } pub fn hname(&self) -> ScMutableHname { - ScMutableHname::new(self.id, idx_map(IDX_PARAM_HNAME)) + ScMutableHname::new(self.id, PARAM_HNAME.get_key_id()) } pub fn int16(&self) -> ScMutableInt16 { - ScMutableInt16::new(self.id, idx_map(IDX_PARAM_INT16)) + ScMutableInt16::new(self.id, PARAM_INT16.get_key_id()) } pub fn int32(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_INT32)) + ScMutableInt32::new(self.id, PARAM_INT32.get_key_id()) } pub fn int64(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_PARAM_INT64)) + ScMutableInt64::new(self.id, PARAM_INT64.get_key_id()) } pub fn int8(&self) -> ScMutableInt8 { - ScMutableInt8::new(self.id, idx_map(IDX_PARAM_INT8)) + ScMutableInt8::new(self.id, PARAM_INT8.get_key_id()) } pub fn param(&self) -> MapStringToMutableBytes { @@ -265,27 +265,27 @@ impl MutableParamTypesParams { } pub fn request_id(&self) -> ScMutableRequestID { - ScMutableRequestID::new(self.id, idx_map(IDX_PARAM_REQUEST_ID)) + ScMutableRequestID::new(self.id, PARAM_REQUEST_ID.get_key_id()) } pub fn string(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_STRING)) + ScMutableString::new(self.id, PARAM_STRING.get_key_id()) } pub fn uint16(&self) -> ScMutableUint16 { - ScMutableUint16::new(self.id, idx_map(IDX_PARAM_UINT16)) + ScMutableUint16::new(self.id, PARAM_UINT16.get_key_id()) } pub fn uint32(&self) -> ScMutableUint32 { - ScMutableUint32::new(self.id, idx_map(IDX_PARAM_UINT32)) + ScMutableUint32::new(self.id, PARAM_UINT32.get_key_id()) } pub fn uint64(&self) -> ScMutableUint64 { - ScMutableUint64::new(self.id, idx_map(IDX_PARAM_UINT64)) + ScMutableUint64::new(self.id, PARAM_UINT64.get_key_id()) } pub fn uint8(&self) -> ScMutableUint8 { - ScMutableUint8::new(self.id, idx_map(IDX_PARAM_UINT8)) + ScMutableUint8::new(self.id, PARAM_UINT8.get_key_id()) } } @@ -296,11 +296,11 @@ pub struct ImmutableTriggerEventParams { impl ImmutableTriggerEventParams { pub fn address(&self) -> ScImmutableAddress { - ScImmutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScImmutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -311,11 +311,11 @@ pub struct MutableTriggerEventParams { impl MutableTriggerEventParams { pub fn address(&self) -> ScMutableAddress { - ScMutableAddress::new(self.id, idx_map(IDX_PARAM_ADDRESS)) + ScMutableAddress::new(self.id, PARAM_ADDRESS.get_key_id()) } pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -326,7 +326,7 @@ pub struct ImmutableArrayLengthParams { impl ImmutableArrayLengthParams { pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -337,7 +337,7 @@ pub struct MutableArrayLengthParams { impl MutableArrayLengthParams { pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -348,11 +348,11 @@ pub struct ImmutableArrayValueParams { impl ImmutableArrayValueParams { pub fn index(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_INDEX)) + ScImmutableInt32::new(self.id, PARAM_INDEX.get_key_id()) } pub fn name(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -363,11 +363,11 @@ pub struct MutableArrayValueParams { impl MutableArrayValueParams { pub fn index(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_INDEX)) + ScMutableInt32::new(self.id, PARAM_INDEX.get_key_id()) } pub fn name(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_NAME)) + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) } } @@ -378,11 +378,11 @@ pub struct ImmutableBlockRecordParams { impl ImmutableBlockRecordParams { pub fn block_index(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_BLOCK_INDEX)) + ScImmutableInt32::new(self.id, PARAM_BLOCK_INDEX.get_key_id()) } pub fn record_index(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_RECORD_INDEX)) + ScImmutableInt32::new(self.id, PARAM_RECORD_INDEX.get_key_id()) } } @@ -393,11 +393,11 @@ pub struct MutableBlockRecordParams { impl MutableBlockRecordParams { pub fn block_index(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_BLOCK_INDEX)) + ScMutableInt32::new(self.id, PARAM_BLOCK_INDEX.get_key_id()) } pub fn record_index(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_RECORD_INDEX)) + ScMutableInt32::new(self.id, PARAM_RECORD_INDEX.get_key_id()) } } @@ -408,7 +408,7 @@ pub struct ImmutableBlockRecordsParams { impl ImmutableBlockRecordsParams { pub fn block_index(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_PARAM_BLOCK_INDEX)) + ScImmutableInt32::new(self.id, PARAM_BLOCK_INDEX.get_key_id()) } } @@ -419,6 +419,6 @@ pub struct MutableBlockRecordsParams { impl MutableBlockRecordsParams { pub fn block_index(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_PARAM_BLOCK_INDEX)) + ScMutableInt32::new(self.id, PARAM_BLOCK_INDEX.get_key_id()) } } diff --git a/contracts/wasm/testwasmlib/src/results.rs b/contracts/wasm/testwasmlib/src/results.rs index 528943d5aa..6b285e9068 100644 --- a/contracts/wasm/testwasmlib/src/results.rs +++ b/contracts/wasm/testwasmlib/src/results.rs @@ -22,7 +22,7 @@ pub struct ImmutableArrayLengthResults { impl ImmutableArrayLengthResults { pub fn length(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_RESULT_LENGTH)) + ScImmutableInt32::new(self.id, RESULT_LENGTH.get_key_id()) } } @@ -33,7 +33,7 @@ pub struct MutableArrayLengthResults { impl MutableArrayLengthResults { pub fn length(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_RESULT_LENGTH)) + ScMutableInt32::new(self.id, RESULT_LENGTH.get_key_id()) } } @@ -44,7 +44,7 @@ pub struct ImmutableArrayValueResults { impl ImmutableArrayValueResults { pub fn value(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_RESULT_VALUE)) + ScImmutableString::new(self.id, RESULT_VALUE.get_key_id()) } } @@ -55,7 +55,7 @@ pub struct MutableArrayValueResults { impl MutableArrayValueResults { pub fn value(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_RESULT_VALUE)) + ScMutableString::new(self.id, RESULT_VALUE.get_key_id()) } } @@ -66,7 +66,7 @@ pub struct ImmutableBlockRecordResults { impl ImmutableBlockRecordResults { pub fn record(&self) -> ScImmutableBytes { - ScImmutableBytes::new(self.id, idx_map(IDX_RESULT_RECORD)) + ScImmutableBytes::new(self.id, RESULT_RECORD.get_key_id()) } } @@ -77,7 +77,7 @@ pub struct MutableBlockRecordResults { impl MutableBlockRecordResults { pub fn record(&self) -> ScMutableBytes { - ScMutableBytes::new(self.id, idx_map(IDX_RESULT_RECORD)) + ScMutableBytes::new(self.id, RESULT_RECORD.get_key_id()) } } @@ -88,7 +88,7 @@ pub struct ImmutableBlockRecordsResults { impl ImmutableBlockRecordsResults { pub fn count(&self) -> ScImmutableInt32 { - ScImmutableInt32::new(self.id, idx_map(IDX_RESULT_COUNT)) + ScImmutableInt32::new(self.id, RESULT_COUNT.get_key_id()) } } @@ -99,7 +99,7 @@ pub struct MutableBlockRecordsResults { impl MutableBlockRecordsResults { pub fn count(&self) -> ScMutableInt32 { - ScMutableInt32::new(self.id, idx_map(IDX_RESULT_COUNT)) + ScMutableInt32::new(self.id, RESULT_COUNT.get_key_id()) } } @@ -110,7 +110,7 @@ pub struct ImmutableGetRandomResults { impl ImmutableGetRandomResults { pub fn random(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_RANDOM)) + ScImmutableInt64::new(self.id, RESULT_RANDOM.get_key_id()) } } @@ -121,7 +121,7 @@ pub struct MutableGetRandomResults { impl MutableGetRandomResults { pub fn random(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_RANDOM)) + ScMutableInt64::new(self.id, RESULT_RANDOM.get_key_id()) } } @@ -132,7 +132,7 @@ pub struct ImmutableIotaBalanceResults { impl ImmutableIotaBalanceResults { pub fn iotas(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_IOTAS)) + ScImmutableInt64::new(self.id, RESULT_IOTAS.get_key_id()) } } @@ -143,6 +143,6 @@ pub struct MutableIotaBalanceResults { impl MutableIotaBalanceResults { pub fn iotas(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_IOTAS)) + ScMutableInt64::new(self.id, RESULT_IOTAS.get_key_id()) } } diff --git a/contracts/wasm/testwasmlib/src/state.rs b/contracts/wasm/testwasmlib/src/state.rs index 80d6c0b01a..0cf46c6c3e 100644 --- a/contracts/wasm/testwasmlib/src/state.rs +++ b/contracts/wasm/testwasmlib/src/state.rs @@ -34,12 +34,12 @@ pub struct ImmutableTestWasmLibState { impl ImmutableTestWasmLibState { pub fn arrays(&self) -> MapStringToImmutableStringArray { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_ARRAYS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_ARRAYS.get_key_id(), TYPE_MAP); MapStringToImmutableStringArray { obj_id: map_id } } pub fn random(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_RANDOM)) + ScImmutableInt64::new(self.id, STATE_RANDOM.get_key_id()) } } @@ -70,11 +70,11 @@ impl MutableTestWasmLibState { } pub fn arrays(&self) -> MapStringToMutableStringArray { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_ARRAYS), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_ARRAYS.get_key_id(), TYPE_MAP); MapStringToMutableStringArray { obj_id: map_id } } pub fn random(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_RANDOM)) + ScMutableInt64::new(self.id, STATE_RANDOM.get_key_id()) } } diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm b/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm index d97c02b6bc91945ccb1f9d9146d457f9ef257c8f..139f4a2b43e3681430d61b7769a9c2fa2335cbfc 100644 GIT binary patch delta 8691 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 n5;Y|ovFZ3B7{uq{m<-sL7 ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_RESULT_TIMESTAMP)) + ScImmutableInt64::new(self.id, RESULT_TIMESTAMP.get_key_id()) } } @@ -32,6 +32,6 @@ pub struct MutableGetTimestampResults { impl MutableGetTimestampResults { pub fn timestamp(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_RESULT_TIMESTAMP)) + ScMutableInt64::new(self.id, RESULT_TIMESTAMP.get_key_id()) } } diff --git a/contracts/wasm/timestamp/src/state.rs b/contracts/wasm/timestamp/src/state.rs index bc097e03fa..3b05d63230 100644 --- a/contracts/wasm/timestamp/src/state.rs +++ b/contracts/wasm/timestamp/src/state.rs @@ -21,7 +21,7 @@ pub struct ImmutabletimestampState { impl ImmutabletimestampState { pub fn timestamp(&self) -> ScImmutableInt64 { - ScImmutableInt64::new(self.id, idx_map(IDX_STATE_TIMESTAMP)) + ScImmutableInt64::new(self.id, STATE_TIMESTAMP.get_key_id()) } } @@ -36,6 +36,6 @@ impl MutabletimestampState { } pub fn timestamp(&self) -> ScMutableInt64 { - ScMutableInt64::new(self.id, idx_map(IDX_STATE_TIMESTAMP)) + ScMutableInt64::new(self.id, STATE_TIMESTAMP.get_key_id()) } } diff --git a/contracts/wasm/timestamp/test/timestamp_bg.wasm b/contracts/wasm/timestamp/test/timestamp_bg.wasm index 6b26a708a35fce116451a5a7cd3cb19c370e4ecd..9c3c5305198bb471be4543d3523d8a64da97d14e 100644 GIT binary patch delta 5070 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 gI{fh*r ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_DESCRIPTION)) + ScImmutableString::new(self.id, PARAM_DESCRIPTION.get_key_id()) } pub fn user_defined(&self) -> ScImmutableString { - ScImmutableString::new(self.id, idx_map(IDX_PARAM_USER_DEFINED)) + ScImmutableString::new(self.id, PARAM_USER_DEFINED.get_key_id()) } } @@ -37,11 +37,11 @@ pub struct MutableMintSupplyParams { impl MutableMintSupplyParams { pub fn description(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_DESCRIPTION)) + ScMutableString::new(self.id, PARAM_DESCRIPTION.get_key_id()) } pub fn user_defined(&self) -> ScMutableString { - ScMutableString::new(self.id, idx_map(IDX_PARAM_USER_DEFINED)) + ScMutableString::new(self.id, PARAM_USER_DEFINED.get_key_id()) } } @@ -52,7 +52,7 @@ pub struct ImmutableTransferOwnershipParams { impl ImmutableTransferOwnershipParams { pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScImmutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -63,7 +63,7 @@ pub struct MutableTransferOwnershipParams { impl MutableTransferOwnershipParams { pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScMutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -74,7 +74,7 @@ pub struct ImmutableUpdateMetadataParams { impl ImmutableUpdateMetadataParams { pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScImmutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -85,7 +85,7 @@ pub struct MutableUpdateMetadataParams { impl MutableUpdateMetadataParams { pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScMutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -96,7 +96,7 @@ pub struct ImmutableGetInfoParams { impl ImmutableGetInfoParams { pub fn color(&self) -> ScImmutableColor { - ScImmutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScImmutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } @@ -107,6 +107,6 @@ pub struct MutableGetInfoParams { impl MutableGetInfoParams { pub fn color(&self) -> ScMutableColor { - ScMutableColor::new(self.id, idx_map(IDX_PARAM_COLOR)) + ScMutableColor::new(self.id, PARAM_COLOR.get_key_id()) } } diff --git a/contracts/wasm/tokenregistry/src/state.rs b/contracts/wasm/tokenregistry/src/state.rs index 9bf3a71326..786e448ff1 100644 --- a/contracts/wasm/tokenregistry/src/state.rs +++ b/contracts/wasm/tokenregistry/src/state.rs @@ -48,12 +48,12 @@ pub struct ImmutableTokenRegistryState { impl ImmutableTokenRegistryState { pub fn color_list(&self) -> ArrayOfImmutableColor { - let arr_id = get_object_id(self.id, idx_map(IDX_STATE_COLOR_LIST), TYPE_ARRAY | TYPE_COLOR); + let arr_id = get_object_id(self.id, STATE_COLOR_LIST.get_key_id(), TYPE_ARRAY | TYPE_COLOR); ArrayOfImmutableColor { obj_id: arr_id } } pub fn registry(&self) -> MapColorToImmutableToken { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_REGISTRY), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_REGISTRY.get_key_id(), TYPE_MAP); MapColorToImmutableToken { obj_id: map_id } } } @@ -103,12 +103,12 @@ impl MutableTokenRegistryState { } pub fn color_list(&self) -> ArrayOfMutableColor { - let arr_id = get_object_id(self.id, idx_map(IDX_STATE_COLOR_LIST), TYPE_ARRAY | TYPE_COLOR); + let arr_id = get_object_id(self.id, STATE_COLOR_LIST.get_key_id(), TYPE_ARRAY | TYPE_COLOR); ArrayOfMutableColor { obj_id: arr_id } } pub fn registry(&self) -> MapColorToMutableToken { - let map_id = get_object_id(self.id, idx_map(IDX_STATE_REGISTRY), TYPE_MAP); + let map_id = get_object_id(self.id, STATE_REGISTRY.get_key_id(), TYPE_MAP); MapColorToMutableToken { obj_id: map_id } } } diff --git a/contracts/wasm/tokenregistry/test/tokenregistry_bg.wasm b/contracts/wasm/tokenregistry/test/tokenregistry_bg.wasm index 39244943da12f642aae2ce320deffcfb1f6427b5..579138571338e56b21213e9a6f2c79c3b9e463c7 100644 GIT binary patch delta 6238 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 delta 6390 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 eKW^jnQuJAgj=o2pZ#7dG=KgF&@9pA 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 WqmO=UvQd#>!~ZFP-U%h`1k+eORDSW>gww1 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 Date: Wed, 5 Jan 2022 20:47:15 -0800 Subject: [PATCH 056/120] Cleared debug flags --- packages/vm/wasmsolo/solocontext.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vm/wasmsolo/solocontext.go b/packages/vm/wasmsolo/solocontext.go index b3446669d2..479ce39b22 100644 --- a/packages/vm/wasmsolo/solocontext.go +++ b/packages/vm/wasmsolo/solocontext.go @@ -24,8 +24,8 @@ import ( ) const ( - SoloDebug = true - SoloHostTracing = true + SoloDebug = false + SoloHostTracing = false SoloStackTracing = false ) From 4d12f861ad5c7d8abc6fae2dbbb0b64b02a175ad Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Fri, 7 Jan 2022 00:00:00 +0530 Subject: [PATCH 057/120] updated tutorial link and added more links --- .../docs/guide/chains_and_nodes/running-a-node.md | 2 +- documentation/docs/guide/evm/create-chain.md | 4 ++++ documentation/docs/guide/evm/tooling.md | 8 ++++++++ documentation/docs/guide/schema/usage.mdx | 8 ++++++++ documentation/docs/guide/wasm_vm/intro.mdx | 3 +++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/documentation/docs/guide/chains_and_nodes/running-a-node.md b/documentation/docs/guide/chains_and_nodes/running-a-node.md index 40e4e85728..71f89d6936 100644 --- a/documentation/docs/guide/chains_and_nodes/running-a-node.md +++ b/documentation/docs/guide/chains_and_nodes/running-a-node.md @@ -245,4 +245,4 @@ wasp --webapi.adminWhitelist=127.0.0.1,YOUR_IP ## Video Tutorial - + diff --git a/documentation/docs/guide/evm/create-chain.md b/documentation/docs/guide/evm/create-chain.md index f35e3819c7..57adfc6031 100644 --- a/documentation/docs/guide/evm/create-chain.md +++ b/documentation/docs/guide/evm/create-chain.md @@ -79,3 +79,7 @@ Re-using an existing Chain ID is not recommended, and can be a security risk. Fo The current implementation uses the wasp-cli's saves the account to pay for any fees in IOTA while using the JSON-RPC interface. This is of course not ideal in a normal usage scenario. Upcoming releases will address this in a more user-friendly way. ::: + +## Video Tutorial + + diff --git a/documentation/docs/guide/evm/tooling.md b/documentation/docs/guide/evm/tooling.md index ee957c8f09..a71cd09299 100644 --- a/documentation/docs/guide/evm/tooling.md +++ b/documentation/docs/guide/evm/tooling.md @@ -52,6 +52,10 @@ If you wish to use additional EVM chains with Metamask, you can simply add more If you also want to use the [Remix IDE](https://remix.ethereum.org/) to deploy any regular Solidity Smart Contract, you should set the environment as **Injected Web3**, which should then connect with your MetaMask wallet. +## Video Tutorial + + + ## Hardhat [Hardhat](https://hardhat.org/) is a commandline toolbox that allows you to deploy, test, verify, and interact with Solidity smart contracts on an EVM chain. EVM chains running on IOTA Smart Contracts are compatible with Hardhat; simply make sure you add the correct network parameters to your `hardhat.config.js`, for example: @@ -73,6 +77,10 @@ Currently, there is no validation service available for EVM/Solidity smart contr ::: +## Video Tutorial + + + ## Ethers.js/Web3.js diff --git a/documentation/docs/guide/schema/usage.mdx b/documentation/docs/guide/schema/usage.mdx index 076bf53a9a..22811f06d7 100644 --- a/documentation/docs/guide/schema/usage.mdx +++ b/documentation/docs/guide/schema/usage.mdx @@ -338,3 +338,11 @@ parameter. In the next section we will look at how a smart contract uses [Structured Data Types](structs.mdx). + +## Video Tutorial + + + +### Creating Smart Contracts using AssemblyScript + + diff --git a/documentation/docs/guide/wasm_vm/intro.mdx b/documentation/docs/guide/wasm_vm/intro.mdx index 1177a97023..a04246a558 100644 --- a/documentation/docs/guide/wasm_vm/intro.mdx +++ b/documentation/docs/guide/wasm_vm/intro.mdx @@ -86,3 +86,6 @@ languages, without having to dive deeply into all aspects of the chosen language We will now dive deeper into the concepts that are central to WasmLib smart contract programming. +## Video Tutorial + + From 692a3facd2e8963b179edb9ed55554fa47bcaee9 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 6 Jan 2022 11:24:25 -0800 Subject: [PATCH 058/120] Fixed problem in transfer.ts. Map does not play nice with Buffer keys. --- packages/vm/wasmlib/ts/wasmclient/transfer.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/transfer.ts b/packages/vm/wasmlib/ts/wasmclient/transfer.ts index c8784c32e3..b082d0173c 100644 --- a/packages/vm/wasmlib/ts/wasmclient/transfer.ts +++ b/packages/vm/wasmlib/ts/wasmclient/transfer.ts @@ -6,7 +6,7 @@ import {Base58} from "./crypto"; import {Buffer} from "./buffer"; export class Transfer { - private xfer = new Map(); + private xfer = new Map(); static iotas(amount: wasmclient.Uint64): Transfer { return Transfer.tokens("IOTA", amount); @@ -22,7 +22,7 @@ export class Transfer { if (color == "IOTA") { color = "11111111111111111111111111111111" } - this.xfer.set(Base58.decode(color), amount); + this.xfer.set(color, amount); } // Encode returns a byte array that encodes the Transfer as follows: @@ -32,25 +32,26 @@ export class Transfer { // transfer count. Next for each color emit the 32-byte color value, // and then the 8-byte amount. encode(): wasmclient.Bytes { - const keys = new Array(); + const keys = new Array(); for (const [key, val] of this.xfer) { // filter out zero transfers if (val != BigInt(0)) { keys.push(key); } } - keys.sort((lhs, rhs) => lhs.compare(rhs)); + keys.sort((lhs, rhs) => lhs.localeCompare(rhs)); let buf = Buffer.alloc(4); buf.writeUInt32LE(keys.length, 0); for (const key of keys) { const val = this.xfer.get(key); if (!val) { - throw new Error("Transfer.encode: missing amount"); + throw new Error("Transfer.encode: missing amount?"); } + const keyBuf = Base58.decode(key); const valBuf = Buffer.alloc(8); valBuf.writeBigUInt64LE(val, 0); - buf = Buffer.concat([buf, key, valBuf]); + buf = Buffer.concat([buf, keyBuf, valBuf]); } return buf; } From 0979d66b0fb02cdcbcfa94e5162f04a1b92f3aa6 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 6 Jan 2022 11:51:14 -0800 Subject: [PATCH 059/120] Force regeneration when schema tool is newer --- tools/schema/main.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tools/schema/main.go b/tools/schema/main.go index e4662475a6..3595845ca3 100644 --- a/tools/schema/main.go +++ b/tools/schema/main.go @@ -98,21 +98,29 @@ func generateCoreInterfaces() { } func generateSchema(file *os.File) error { - info, err := file.Stat() - if err != nil { - return err - } - s, err := loadSchema(file) if err != nil { return err } s.CoreContracts = *flagCore - s.SchemaTime = info.ModTime() - if *flagForce { - // make as if it has just been updated - s.SchemaTime = time.Now() + s.SchemaTime = time.Now() + if !*flagForce { + // force regeneration when schema definition file is newer + info, err := file.Stat() + if err != nil { + return err + } + s.SchemaTime = info.ModTime() + + // also force regeneration when schema tool itself is newer + info, err = os.Stat(os.Args[0]) + if err != nil { + return err + } + if info.ModTime().After(s.SchemaTime) { + s.SchemaTime = info.ModTime() + } } if *flagGo { From 67af9167e14207b8ba7d7a9a116ac6524876f29e Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Thu, 6 Jan 2022 23:53:04 +0100 Subject: [PATCH 060/120] Implemented deposit from L1 to chain --- .../vm/wasmlib/ts/wasmclient/arguments.ts | 10 + .../vm/wasmlib/ts/wasmclient/clientfunc.ts | 20 +- packages/vm/wasmlib/ts/wasmclient/colors.ts | 9 + .../core_services/accounts_service.ts | 28 +++ .../wasmclient/goshimmer/goshimmerclient.ts | 119 ++++++--- .../wasmclient/goshimmer/models/on_ledger.ts | 45 ++++ .../goshimmer/models/transaction.ts | 182 ++++++++++++++ .../goshimmer/utils/simple_buffer_cursor.ts | 89 +++++++ .../ts/wasmclient/goshimmer/wallet/wallet.ts | 231 ++++++++++++++++++ .../goshimmer/wallet/wallet_models.ts | 40 +++ packages/vm/wasmlib/ts/wasmclient/index.ts | 1 + packages/vm/wasmlib/ts/wasmclient/service.ts | 104 ++++---- .../vm/wasmlib/ts/wasmclient/serviceclient.ts | 18 +- packages/vm/wasmlib/ts/wasmclient/transfer.ts | 7 + .../vm/wasmlib/ts/wasmclient/waspclient.ts | 131 ++++------ 15 files changed, 841 insertions(+), 193 deletions(-) create mode 100644 packages/vm/wasmlib/ts/wasmclient/colors.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/core_services/accounts_service.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/models/on_ledger.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/models/transaction.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/utils/simple_buffer_cursor.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/wallet/wallet.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/goshimmer/wallet/wallet_models.ts diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 68065695fc..23b03be0a5 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -10,6 +10,11 @@ import {Buffer} from "./buffer"; export class Arguments { private args = new Map(); + get(key: string): wasmclient.Bytes { + const bytes = this.args.get(key); + return bytes ?? Buffer.alloc(0); + } + private set(key: string, val: wasmclient.Bytes): void { this.args.set(key, val); } @@ -96,6 +101,11 @@ export class Arguments { this.setBase58(key, val, wasmclient.TYPE_REQUEST_ID); } + getString(key: string): string { + const bytes = this.get(key); + return bytes.toString(); + } + setString(key: string, val: string): void { this.set(key, Buffer.from(val)); } diff --git a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts index 82b06000c4..98b6fbf79d 100644 --- a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts +++ b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts @@ -1,8 +1,8 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import * as wasmclient from "./index" -import {IKeyPair} from "./crypto"; +import * as wasmclient from "./index"; +import { IKeyPair } from "./crypto"; export class ClientFunc { protected svc: wasmclient.Service; @@ -16,16 +16,14 @@ export class ClientFunc { // Sends a request to the smart contract service // You can wait for the request to complete by using the returned RequestID // as parameter to Service.waitRequest() - public async post(hFuncName: wasmclient.Hname, args: wasmclient.Arguments | null): Promise { - if (!args) - args = new wasmclient.Arguments(); - - if (!this.keyPair) - this.keyPair = this.svc.keyPair; - + public async post(hFuncName: wasmclient.Hname, args: wasmclient.Arguments | null, offLedger: boolean = true): Promise { + if (!args) args = new wasmclient.Arguments(); + + if (!this.keyPair) this.keyPair = this.svc.keyPair; + if (!this.keyPair) throw new Error("Key pair not defined"); - return await this.svc.postRequest(hFuncName, args, this.xfer, this.keyPair); + return await this.svc.postRequest(hFuncName, args, this.xfer, this.keyPair, offLedger); } // Optionally overrides the default keypair from the service @@ -35,7 +33,7 @@ export class ClientFunc { // Optionally indicates which tokens to transfer as part of the request // The tokens are presumed to be available in the signing account on the chain - public transfer(xfer: wasmclient.Transfer): void { + public transfer(xfer: wasmclient.Transfer): void { this.xfer = xfer; } } diff --git a/packages/vm/wasmlib/ts/wasmclient/colors.ts b/packages/vm/wasmlib/ts/wasmclient/colors.ts new file mode 100644 index 0000000000..475b229cb6 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/colors.ts @@ -0,0 +1,9 @@ +import { Buffer } from "./buffer"; + +export class Colors { + public static readonly IOTA_COLOR: string = "IOTA"; + public static readonly IOTA_COLOR_STRING: string = "11111111111111111111111111111111"; + public static readonly IOTA_COLOR_BYTES: Buffer = Buffer.alloc(32); +} + +export type ColorCollection = { [key: string]: bigint }; diff --git a/packages/vm/wasmlib/ts/wasmclient/core_services/accounts_service.ts b/packages/vm/wasmlib/ts/wasmclient/core_services/accounts_service.ts new file mode 100644 index 0000000000..f27ec11488 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/core_services/accounts_service.ts @@ -0,0 +1,28 @@ +import { Arguments, ClientFunc, RequestID } from ".."; +import { EventHandlers, Service } from "../service"; +import { ServiceClient } from "../serviceclient"; + +export class AccountsService extends Service { + public constructor(cl: ServiceClient) { + const emptyEventHandlers: EventHandlers = new Map(); + super(cl, 0x3c4b5e02, emptyEventHandlers); + } + + public deposit(): DepositFunc { + return new DepositFunc(this); + } +} + +export class DepositFunc extends ClientFunc { + private ArgAddress: string = "address"; + + private args: Arguments = new Arguments(); + + public address(v: string): void { + this.args.setString(this.ArgAddress, v); + } + + public async post(): Promise { + return await super.post(0xbdc9102d, this.args, false); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts index 1cfe80c550..8b9e15cbaf 100644 --- a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts @@ -1,39 +1,47 @@ -import { Buffer } from '../buffer'; -import { Configuration } from '../configuration'; +import { Buffer } from "../buffer"; -import { IFaucetRequest, IFaucetRequestContext, IFaucetResponse } from './faucet/faucet_models'; -import { FaucetHelper } from './faucet/faucet_helper'; +import { IFaucetRequest, IFaucetRequestContext, IFaucetResponse } from "./faucet/faucet_models"; +import { FaucetHelper } from "./faucet/faucet_helper"; -import { IUnspentOutputsRequest, IUnspentOutputsResponse } from './models/unspent_outputs'; -import { IAllowedManaPledgeResponse } from './models/mana'; +import { IUnspentOutputsRequest, IUnspentOutputsResponse } from "./models/unspent_outputs"; +import { IAllowedManaPledgeResponse } from "./models/mana"; -import { PoWWorkerManager } from './pow_web_worker/pow_worker_manager'; +import { PoWWorkerManager } from "./pow_web_worker/pow_worker_manager"; -import * as requestSender from '../api_common/request_sender'; +import * as requestSender from "../api_common/request_sender"; + +import { Base58, IKeyPair } from "../crypto"; + +import { IOnLedger, OnLedgerHelper } from "./models/on_ledger"; +import { ISendTransactionRequest, ISendTransactionResponse, ITransaction, Transaction } from "./models/transaction"; +import { Wallet } from "./wallet/wallet"; +import { Colors } from "../colors"; +import { Configuration, Transfer } from ".."; +import { AccountsService } from "../core_services/accounts_service"; interface GoShimmerClientConfiguration { APIUrl: string; SeedUnsafe: Buffer | null; } -const IOTA_COLOR: string = 'IOTA'; - export class GoShimmerClient { + private accountsService: AccountsService; private readonly goShimmerConfiguration: GoShimmerClientConfiguration; private readonly powManager: PoWWorkerManager = new PoWWorkerManager(); - constructor(configuration: Configuration) { + constructor(configuration : Configuration, accountsService: AccountsService) { + this.accountsService = accountsService; this.goShimmerConfiguration = { APIUrl: configuration.goShimmerApiUrl, SeedUnsafe: configuration.seed }; } public async getIOTABalance(address: string): Promise { - const iotaBalance = await this.getBalance(address, IOTA_COLOR); + const iotaBalance = await this.getBalance(address, Colors.IOTA_COLOR_STRING); return iotaBalance; } private async getBalance(address: string, color: string): Promise { - if (color == IOTA_COLOR) { - color = '11111111111111111111111111111111'; + if (color == Colors.IOTA_COLOR) { + color = Colors.IOTA_COLOR_STRING; } const unspents = await this.unspentOutputs({ addresses: [address] }); @@ -42,8 +50,8 @@ export class GoShimmerClient { const balance = currentUnspent!.outputs .filter( (o) => - ['ExtendedLockedOutputType', 'SigLockedColoredOutputType'].includes(o.output.type) && - typeof o.output.output.balances[color] != 'undefined' + ["ExtendedLockedOutputType", "SigLockedColoredOutputType"].includes(o.output.type) && + typeof o.output.output.balances[color] != "undefined" ) .map((uid) => uid.output.output.balances) .reduce((balance: bigint, output) => (balance += BigInt(output[color])), BigInt(0)); @@ -51,11 +59,11 @@ export class GoShimmerClient { return balance; } - private async unspentOutputs(request: IUnspentOutputsRequest): Promise { + public async unspentOutputs(request: IUnspentOutputsRequest): Promise { return requestSender.sendRequest( this.goShimmerConfiguration.APIUrl, - 'post', - 'ledgerstate/addresses/unspentOutputs', + "post", + "ledgerstate/addresses/unspentOutputs", request ); } @@ -76,8 +84,8 @@ export class GoShimmerClient { private async getFaucetRequest(address: string): Promise { const manaPledge = await this.getAllowedManaPledge(); - const allowedManagePledge = manaPledge.accessMana?.allowed ? manaPledge.accessMana.allowed[0] : ''; - const consenseusManaPledge = manaPledge.consensusMana?.allowed ? manaPledge.consensusMana?.allowed[0] : ''; + const allowedManagePledge = manaPledge.accessMana?.allowed ? manaPledge.accessMana.allowed[0] : ""; + const consenseusManaPledge = manaPledge.consensusMana?.allowed ? manaPledge.consensusMana?.allowed[0] : ""; const body: IFaucetRequest = { accessManaPledgeID: allowedManagePledge, @@ -99,20 +107,75 @@ export class GoShimmerClient { } private async getAllowedManaPledge(): Promise { - return requestSender.sendRequest( - this.goShimmerConfiguration.APIUrl, - 'get', - 'mana/allowedManaPledge' - ); + return requestSender.sendRequest(this.goShimmerConfiguration.APIUrl, "get", "mana/allowedManaPledge"); } private async sendFaucetRequest(faucetRequest: IFaucetRequest): Promise { const response = await requestSender.sendRequest( this.goShimmerConfiguration.APIUrl, - 'post', - 'faucet', + "post", + "faucet", faucetRequest ); return response; } + + public async sendOnLedgerRequest( + address: string, + chainId: string, + payload: IOnLedger, + transfer: bigint = 1n, + keyPair: IKeyPair + ): Promise { + if (transfer <= 0) { + transfer = 1n; + } + + const wallet = new Wallet(this); + + const unspents = await wallet.getUnspentOutputs(address); + const consumedOutputs = wallet.determineOutputsToConsume(unspents, transfer); + const { inputs, consumedFunds } = wallet.buildInputs(consumedOutputs); + const outputs = wallet.buildOutputs(address, chainId, transfer, consumedFunds); + + const tx: ITransaction = { + version: 0, + timestamp: BigInt(Date.now()) * 1000000n, + aManaPledge: Base58.encode(Buffer.alloc(32)), + cManaPledge: Base58.encode(Buffer.alloc(32)), + inputs: inputs, + outputs: outputs, + chainId: chainId, + payload: OnLedgerHelper.ToBuffer(payload), + unlockBlocks: [], + }; + + tx.unlockBlocks = wallet.unlockBlocks(tx, keyPair, address, consumedOutputs, inputs); + + const result = Transaction.bytes(tx); + + const response = await this.sendTransaction({ + txn_bytes: result.toString("base64"), + }); + + if (!response || response.error != undefined) throw Error("Transaction error: " + response.error); + return response?.transaction_id ?? ""; + } + + private async sendTransaction(request: ISendTransactionRequest): Promise { + return requestSender.sendRequest( + this.goShimmerConfiguration.APIUrl, + "post", + "ledgerstate/transactions", + request + ); + } + + public async depositToAccountInChain(keypair: IKeyPair, destinationAddress: string, amount: bigint) { + const depositfunc = this.accountsService.deposit(); + depositfunc.address(destinationAddress); + depositfunc.transfer(Transfer.iotas(amount)); + depositfunc.sign(keypair); + depositfunc.post(); + } } diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/on_ledger.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/on_ledger.ts new file mode 100644 index 0000000000..6dead8f361 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/on_ledger.ts @@ -0,0 +1,45 @@ +import { Buffer } from "../../buffer"; +import { SimpleBufferCursor } from "../utils/simple_buffer_cursor"; + +export interface OnLedgerArgument { + key: string; + value: number; +} + +export interface IOnLedger { + contract?: number; + entrypoint?: number; + arguments?: OnLedgerArgument[]; + nonce?: number; +} + +export class OnLedgerHelper { + public static ToBuffer(req: IOnLedger): Buffer { + const buffer = new SimpleBufferCursor(Buffer.alloc(0)); + + buffer.writeUInt32LE(0); + buffer.writeUInt32LE(req.contract || 0); + buffer.writeUInt32LE(req.entrypoint || 0); + buffer.writeBytes(Buffer.alloc(1, 0)); + + buffer.writeUInt32LE(req.arguments?.length || 0); + + if (req.arguments) { + req.arguments.sort((lhs, rhs) => lhs.key.localeCompare(rhs.key)); + for (const arg of req.arguments) { + const keyBuffer = Buffer.from(arg.key); + + buffer.writeUInt16LE(keyBuffer.length); + buffer.writeBytes(keyBuffer); + + const valueBuffer = Buffer.alloc(8); + valueBuffer.writeInt32LE(arg.value, 0); + + buffer.writeUInt32LE(valueBuffer.length); + buffer.writeBytes(valueBuffer); + } + } + + return buffer.buffer; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/transaction.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/transaction.ts new file mode 100644 index 0000000000..479b745f3d --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/transaction.ts @@ -0,0 +1,182 @@ +import { Buffer } from "../../buffer"; +import { SimpleBufferCursor } from "../utils/simple_buffer_cursor"; +import { Base58, IKeyPair, ED25519 } from "../../crypto"; + +import type { BuiltOutputResult } from "../wallet/wallet"; + +import { IResponse } from "../../api_common/response_models"; +import { Colors } from "../../colors"; + +export interface ISendTransactionRequest { + txn_bytes: string; +} + +export interface ISendTransactionResponse extends IResponse { + transaction_id?: string; +} + +export interface IUnlockBlock { + type: number; + referenceIndex: number; + publicKey: Buffer; + signature: Buffer; +} + +export interface ITransaction { + /** + * The transaction's version. + */ + version: number; + + /** + * The transaction's timestamp. + */ + timestamp: bigint; + + /** + * The nodeID to pledge access mana. + */ + aManaPledge: string; + + /** + * The nodeID to pledge consensus mana. + */ + cManaPledge: string; + + /** + * The inputs to send. + */ + inputs: string[]; + + payload: Buffer; + + chainId: string; + /** + * The outputs to send. + */ + outputs: BuiltOutputResult; + + /** + * The signatures to send. + */ + unlockBlocks: IUnlockBlock[]; +} + +/** + * Class to help with transactions. + */ +export class Transaction { + /** + * Sign a transaction. + * @param keyPair The key pair to sign with. + * @param buffer The data to sign. + * @returns The signature. + */ + public static sign(keyPair: IKeyPair, buffer: Buffer): Buffer { + return ED25519.privateSign(keyPair, buffer); + } + + /** + * Get the essence for a transaction. + * @param tx The tx to get the essence for. + * @returns The essence of the transaction. + */ + public static essence(tx: ITransaction, payload: Buffer = Buffer.alloc(0)): Buffer { + const essenceBuffer = new SimpleBufferCursor(); + + essenceBuffer.writeInt8(tx.version); + essenceBuffer.writeUInt64LE(tx.timestamp); + essenceBuffer.writeBytes(Base58.decode(tx.aManaPledge)); + essenceBuffer.writeBytes(Base58.decode(tx.cManaPledge)); + + essenceBuffer.writeUInt16LE(tx.inputs.length); + for (const input of tx.inputs) { + essenceBuffer.writeInt8(0); + const decodedInput = Base58.decode(input); + essenceBuffer.writeBytes(decodedInput); + } + + essenceBuffer.writeUInt16LE(Object.keys(tx.outputs).length); + + const outputBuffers: SimpleBufferCursor[] = []; + + for (const address in tx.outputs) { + const outputBuffer = new SimpleBufferCursor(); + + outputBuffer.writeInt8(3); + outputBuffer.writeUInt32LE(tx.outputs[address].length); + + const bufferColors: Buffer[] = []; + + for (const balance of tx.outputs[address]) { + const colorValueBuffer = Buffer.alloc(8); + colorValueBuffer.writeBigUInt64LE(balance.value, 0); + bufferColors.push(Buffer.concat([Colors.IOTA_COLOR_BYTES, colorValueBuffer])); + } + + bufferColors.sort((a, b) => a.compare(b)); + bufferColors.forEach((x) => outputBuffer.writeBytes(x)); + + const decodedAddress = Base58.decode(address); + outputBuffer.writeBytes(decodedAddress); + + if (address == tx.chainId) { + outputBuffer.writeInt8(4); + outputBuffer.writeUInt16LE(tx.payload.length); + outputBuffer.writeBytes(tx.payload); + } else { + outputBuffer.writeInt8(0); + } + + outputBuffers.push(outputBuffer); + } + + outputBuffers.sort((a, b) => a.buffer.compare(b.buffer)); + outputBuffers.forEach((x) => essenceBuffer.writeBytes(x.buffer)); + + essenceBuffer.writeUInt32LE(0); + + return essenceBuffer.buffer; + } + + /** + * Get the bytes for a transaction. + * @param tx The tx to get the bytes for. + * @param essence Existing essence. + * @returns The bytes of the transaction. + */ + public static bytes(tx: ITransaction): Buffer { + const buffer = new SimpleBufferCursor(); + + buffer.writeUInt32LE(1337); + + const essenceBytes = Transaction.essence(tx); + buffer.writeBytes(essenceBytes); + buffer.writeUInt16LE(tx.unlockBlocks.length); + + // TODO: Update this in the next refactoring. + // eslint-disable-next-line @typescript-eslint/no-for-in-array + for (const index in tx.unlockBlocks) { + const ubType = tx.unlockBlocks[index].type; + + buffer.writeInt8(ubType); + + if (ubType === 0) { + buffer.writeInt8(0); + buffer.writeBytes(tx.unlockBlocks[index].publicKey); + buffer.writeBytes(tx.unlockBlocks[index].signature); + + continue; + } + + buffer.writeUInt16LE(tx.unlockBlocks[index].referenceIndex); + } + + const returnBuffer = new SimpleBufferCursor(); + + returnBuffer.writeUInt32LE(buffer.buffer.length); + returnBuffer.writeBytes(buffer.buffer); + + return returnBuffer.buffer; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/utils/simple_buffer_cursor.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/utils/simple_buffer_cursor.ts new file mode 100644 index 0000000000..2e833db40c --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/utils/simple_buffer_cursor.ts @@ -0,0 +1,89 @@ +import { Buffer } from "../../buffer"; + +export class SimpleBufferCursor { + private _buffer: Buffer; + private _traverse: number; + + get buffer(): Buffer { + return this._buffer; + } + + constructor(buffer: Buffer = Buffer.alloc(0)) { + this._buffer = buffer; + this._traverse = 0; + } + + readIntBE(length: number): number { + const value = this._buffer.readIntBE(this._traverse, length); + this._traverse += length; + + return value; + } + + readUInt32LE(): number { + const value = this._buffer.readUInt32LE(this._traverse); + this._traverse += 4; + + return value; + } + + readUInt64LE(): bigint { + const value = this._buffer.readBigUInt64LE(this._traverse); + this._traverse += 8; + + return value; + } + + readUInt16LE(): number { + const value = this._buffer.readUInt16LE(this._traverse); + this._traverse += 2; + + return value; + } + + readBytes(length: number): Uint8Array { + const subBuffer = this._buffer.subarray(this._traverse, this._traverse + length); + this._traverse += length; + + return subBuffer; + } + + writeIntBE(value: number, length: number): void { + const nBuffer = Buffer.alloc(length); + nBuffer.writeIntBE(value, 0, length); + + this._buffer = Buffer.concat([this._buffer, nBuffer]); + } + + writeInt8(value: number): void { + const nBuffer = Buffer.alloc(1); + nBuffer.writeInt8(value, 0); + + this._buffer = Buffer.concat([this._buffer, nBuffer]); + } + + writeUInt32LE(value: number): void { + const nBuffer = Buffer.alloc(4); + nBuffer.writeUInt32LE(value, 0); + + this._buffer = Buffer.concat([this._buffer, nBuffer]); + } + + writeUInt64LE(value: bigint): void { + const nBuffer = Buffer.alloc(8); + nBuffer.writeBigUInt64LE(value, 0); + + this._buffer = Buffer.concat([this._buffer, nBuffer]); + } + + writeUInt16LE(value: number): void { + const nBuffer = Buffer.alloc(2); + nBuffer.writeUInt16LE(value, 0); + + this._buffer = Buffer.concat([this._buffer, nBuffer]); + } + + writeBytes(bytes: Buffer): void { + this._buffer = Buffer.concat([this._buffer, bytes]); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/wallet/wallet.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/wallet/wallet.ts new file mode 100644 index 0000000000..fb7f51e719 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/wallet/wallet.ts @@ -0,0 +1,231 @@ +import { Base58, IKeyPair } from "../../crypto"; +import { Buffer } from "../../buffer"; +import { ITransaction, Transaction, IUnlockBlock } from "../models/transaction"; +import type { GoShimmerClient } from "../goshimmerclient"; +import type { IWalletAddressOutput, IWalletOutput } from "./wallet_models"; +import { ColorCollection, Colors } from "../../colors"; + +export type BuiltOutputResult = { + [address: string]: { + color: string; + value: bigint; + }[]; +}; + +export type ConsumedOutputs = { + [address: string]: { [outputID: string]: IWalletOutput }; +}; + +export class Wallet { + private goShimmerClient: GoShimmerClient; + constructor(client: GoShimmerClient) { + this.goShimmerClient = client; + } + + private fakeBigBalance(balances: ColorCollection) { + const colorCollection: ColorCollection = {}; + + for (const color in balances) { + colorCollection[color] = BigInt(balances[color]); + } + + return colorCollection; + } + + public async getUnspentOutputs(address: string): Promise> { + const unspents = await this.goShimmerClient.unspentOutputs({ addresses: [address] }); + + const usedAddresses = unspents.unspentOutputs.filter((u) => u.outputs.length > 0); + + const unspentOutputs = usedAddresses.map((uo) => ({ + address: uo.address.base58, + outputs: uo.outputs.map((uid) => ({ + id: uid.output.outputID.base58, + balances: this.fakeBigBalance(uid.output.output.balances), + inclusionState: uid.inclusionState, + })), + })); + + return unspentOutputs; + } + + public determineOutputsToConsume(unspentOutputs: IWalletAddressOutput[], iotas: bigint): ConsumedOutputs { + const outputsToConsume: { [address: string]: { [outputID: string]: IWalletOutput } } = {}; + + let iotasLeft = iotas; + + for (const unspentOutput of unspentOutputs) { + let outputsFromAddressSpent = false; + + for (const output of unspentOutput.outputs) { + let requiredColorFoundInOutput = false; + + if (!output.balances[Colors.IOTA_COLOR_STRING]) { + continue; + } + + const balance = output.balances[Colors.IOTA_COLOR_STRING]; + + if (iotasLeft > 0n) { + if (iotasLeft > balance) { + iotasLeft -= balance; + } else { + iotasLeft = 0n; + } + + requiredColorFoundInOutput = true; + } + + // if we found required tokens in this output + if (requiredColorFoundInOutput) { + // store the output in the outputs to use for the transfer + outputsToConsume[unspentOutput.address] = {}; + outputsToConsume[unspentOutput.address][output.id] = output; + + // mark address as spent + outputsFromAddressSpent = true; + } + } + + if (outputsFromAddressSpent) { + for (const output of unspentOutput.outputs) { + outputsToConsume[unspentOutput.address][output.id] = output; + } + } + } + + return outputsToConsume; + } + + public buildOutputs(remainderAddress: string, destinationAddress: string, iotas: bigint, consumedFunds: ColorCollection): BuiltOutputResult { + const outputsByColor: { [address: string]: ColorCollection } = {}; + + // build outputs for destinations + + if (!outputsByColor[destinationAddress]) { + outputsByColor[destinationAddress] = {}; + } + + if (!outputsByColor[destinationAddress][Colors.IOTA_COLOR_STRING]) { + outputsByColor[destinationAddress][Colors.IOTA_COLOR_STRING] = 0n; + } + const t = outputsByColor[destinationAddress][Colors.IOTA_COLOR_STRING]; + outputsByColor[destinationAddress][Colors.IOTA_COLOR_STRING] += iotas; + + consumedFunds[Colors.IOTA_COLOR_STRING] = BigInt(consumedFunds[Colors.IOTA_COLOR_STRING] ?? 0n) - iotas; + if (consumedFunds[Colors.IOTA_COLOR_STRING] === 0n) { + delete consumedFunds[Colors.IOTA_COLOR_STRING]; + } + + // build outputs for remainder + if (Object.keys(consumedFunds).length > 0) { + if (!remainderAddress) { + throw new Error("No remainder address available"); + } + if (!outputsByColor[remainderAddress]) { + outputsByColor[remainderAddress] = {}; + } + for (const consumed in consumedFunds) { + if (!outputsByColor[remainderAddress][consumed]) { + outputsByColor[remainderAddress][consumed] = 0n; + } + outputsByColor[remainderAddress][consumed] += consumedFunds[consumed]; + } + } + + // construct result + const outputsBySlice: BuiltOutputResult = {}; + + for (const address in outputsByColor) { + outputsBySlice[address] = []; + for (const color in outputsByColor[address]) { + outputsBySlice[address].push({ + color, + value: outputsByColor[address][color], + }); + } + } + + return outputsBySlice; + } + + public buildInputs(outputsToUseAsInputs: { [address: string]: { [outputID: string]: IWalletOutput } }): { + /** + * The inputs to send. + */ + inputs: string[]; + /** + * The fund that were consumed. + */ + consumedFunds: ColorCollection; + } { + const inputs: string[] = []; + const consumedFunds: ColorCollection = {}; + + for (const address in outputsToUseAsInputs) { + for (const outputID in outputsToUseAsInputs[address]) { + inputs.push(outputID); + + for (const color in outputsToUseAsInputs[address][outputID].balances) { + const balance = outputsToUseAsInputs[address][outputID].balances[color]; + + if (!consumedFunds[color]) { + consumedFunds[color] = balance; + } else { + consumedFunds[color] += balance; + } + } + } + } + + inputs.sort((a, b) => Base58.decode(a).compare(Base58.decode(b))); + + return { inputs, consumedFunds }; + } + + public unlockBlocks( + tx: ITransaction, + keyPair: IKeyPair, + address: string, + consumedOutputs: ConsumedOutputs, + builtInputs: string[] + ): Array { + const unlockBlocks: IUnlockBlock[] = []; + const txEssence = Transaction.essence(tx, Buffer.alloc(0)); + + const addressByOutputID: { [outputID: string]: string } = {}; + for (const address in consumedOutputs) { + for (const outputID in consumedOutputs[address]) { + addressByOutputID[outputID] = address; + } + } + + const existingUnlockBlocks: { [address: string]: number } = {}; + // TODO: Update this in the next refactoring. + for (const index in builtInputs) { + const addr = address == addressByOutputID[builtInputs[index]]; + if (addr) { + if (existingUnlockBlocks[address] !== undefined) { + unlockBlocks.push({ + type: 1, + referenceIndex: existingUnlockBlocks[address], + publicKey: Buffer.alloc(0), + signature: Buffer.alloc(0), + }); + continue; + } + + const signatureUnlockBlock = { + type: 0, + referenceIndex: 0, + publicKey: keyPair.publicKey, + signature: Transaction.sign(keyPair, txEssence), + }; + existingUnlockBlocks[address] = unlockBlocks.length; + unlockBlocks.push(signatureUnlockBlock); + } + } + + return unlockBlocks; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/wallet/wallet_models.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/wallet/wallet_models.ts new file mode 100644 index 0000000000..dfe7d41b8d --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/wallet/wallet_models.ts @@ -0,0 +1,40 @@ +import { ColorCollection } from "../../colors"; + +export interface IWalletOutputInclusionState { + solid?: boolean; + confirmed?: boolean; + rejected?: boolean; + liked?: boolean; + conflicting?: boolean; + finalized?: boolean; + preferred?: boolean; +} + +export interface IWalletOutput { + /** + * The id. + */ + id: string; + + /** + * The balances. + */ + balances: ColorCollection; + + /** + * Inclusion state. + */ + inclusionState: IWalletOutputInclusionState; +} + +export interface IWalletAddressOutput { + /** + * The address. + */ + address: string; + + /** + * The outputs. + */ + outputs: IWalletOutput[]; +} diff --git a/packages/vm/wasmlib/ts/wasmclient/index.ts b/packages/vm/wasmlib/ts/wasmclient/index.ts index 5747f4997b..a336ab8fcf 100644 --- a/packages/vm/wasmlib/ts/wasmclient/index.ts +++ b/packages/vm/wasmlib/ts/wasmclient/index.ts @@ -11,4 +11,5 @@ export * from "./serviceclient"; export * from "./transfer"; export * from "./types"; export * from "./waspclient"; +export * from "./goshimmer/goshimmerclient"; export * from "./configuration"; diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index dbd8f7901a..b2ce869370 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -1,11 +1,12 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import * as wasmclient from "./index" -import {Base58, ED25519, IKeyPair, Hash} from "./crypto"; -import {Buffer} from "./buffer"; +import * as wasmclient from "./index"; +import { IKeyPair } from "./crypto"; +import { IOnLedger } from "./goshimmer/models/on_ledger"; +import { Colors } from "./colors"; -export type EventHandlers = { [key: string]: (message: string[]) => void }; +export type EventHandlers = Map void>; export class Service { private serviceClient: wasmclient.ServiceClient; @@ -26,49 +27,34 @@ export class Service { this.serviceClient.configuration.chainId, this.scHname.toString(16), viewName, - args.encode(), + args.encode() ); } - public async postRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): Promise { - // get request essence ready for signing - const chainID = this.serviceClient.configuration.chainId; - let essence = Base58.decode(chainID); - const hNames = Buffer.alloc(8) - hNames.writeUInt32LE(this.scHname, 0); - hNames.writeUInt32LE(hFuncName, 4); - const nonce = Buffer.alloc(8) - nonce.writeBigUInt64LE(BigInt(Math.trunc(performance.now())), 0); - essence = Buffer.concat([essence, hNames, args.encode(), keyPair.publicKey, nonce, transfer.encode()]); - - let buf = Buffer.alloc(1); - const requestTypeOffledger = 1; - buf.writeUInt8(requestTypeOffledger, 0); - buf = Buffer.concat([buf, essence, ED25519.privateSign(keyPair, essence)]); - const hash = Hash.from(buf); - const requestID = Buffer.concat([hash, Buffer.alloc(2)]); - - await this.serviceClient.waspClient.postRequest(chainID, buf); - return Base58.encode(requestID); - } - - public async postOnLedgerRequest(hFuncName: wasmclient.Int32, args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair): Promise { - // get request essence ready for signing - const config = this.serviceClient.configuration; - let essence = Buffer.alloc(13) - essence.writeUInt32LE(this.scHname, 4); - essence.writeUInt32LE(hFuncName, 8); - essence = Buffer.concat([essence, args.encode()]); - - let buf = Buffer.alloc(1); - const requestTypeOffledger = 1; - buf.writeUInt8(requestTypeOffledger, 0); - buf = Buffer.concat([buf, essence, ED25519.privateSign(keyPair, essence)]); - const hash = Hash.from(buf); - const requestID = Buffer.concat([hash, Buffer.alloc(2)]); - - await this.serviceClient.waspClient.postOnLedgerRequest(config.chainId, buf); - return Base58.encode(requestID); + public async postRequest( + hFuncName: wasmclient.Int32, + args: wasmclient.Arguments, + transfer: wasmclient.Transfer, + keyPair: IKeyPair, + offLedger: boolean + ): Promise { + const chainId = this.serviceClient.configuration.chainId; + if (offLedger) { + const requestID = await this.serviceClient.waspClient.postOffLedgerRequest(chainId, this.scHname, hFuncName, args, transfer, keyPair); + return requestID; + } else { + const address = args.getString("address"); + const payload: IOnLedger = { + contract: this.scHname, + entrypoint: hFuncName, + //TODO: map args + //arguments : args + }; + const transferAmount = transfer.get(Colors.IOTA_COLOR); + const transactionID = await this.serviceClient.goShimmerClient.sendOnLedgerRequest(address, chainId, payload, transferAmount, keyPair); + if (!transactionID) throw new Error("No transaction id"); + return transactionID; + } } public async waitRequest(reqID: wasmclient.RequestID): Promise { @@ -76,36 +62,40 @@ export class Service { } private configureWebSocketsEventHandlers(eventHandlers: EventHandlers) { - this.eventHandlers = eventHandlers + this.eventHandlers = eventHandlers; - if(this.serviceClient.configuration.waspWebSocketUrl.startsWith("wss://") || this.serviceClient.configuration.waspWebSocketUrl.startsWith("ws://")) + if ( + this.serviceClient.configuration.waspWebSocketUrl.startsWith("wss://") || + this.serviceClient.configuration.waspWebSocketUrl.startsWith("ws://") + ) this.waspWebSocketUrl = this.serviceClient.configuration.waspWebSocketUrl; - else - this.waspWebSocketUrl = "ws://" + this.serviceClient.configuration.waspWebSocketUrl; + else this.waspWebSocketUrl = "ws://" + this.serviceClient.configuration.waspWebSocketUrl; this.waspWebSocketUrl = this.waspWebSocketUrl.replace("%chainId", this.serviceClient.configuration.chainId); - this.connectWebSocket(); + if (this.eventHandlers.size > 1) this.connectWebSocket(); } private connectWebSocket(): void { // eslint-disable-next-line no-console console.log(`Connecting to Websocket => ${this.waspWebSocketUrl}`); this.webSocket = new WebSocket(this.waspWebSocketUrl); - this.webSocket.addEventListener('message', (x) => this.handleIncomingMessage(x)); - this.webSocket.addEventListener('close', () => setTimeout(this.connectWebSocket.bind(this), 1000)); + this.webSocket.addEventListener("message", (x) => this.handleIncomingMessage(x)); + this.webSocket.addEventListener("close", () => setTimeout(this.connectWebSocket.bind(this), 1000)); } private handleIncomingMessage(message: MessageEvent): void { // expect vmmsg contract.event|parameters - const msg = message.data.toString().split(' '); - if (msg.length != 4 || msg[0] != 'vmmsg') { + const msg = message.data.toString().split(" "); + if (msg.length != 4 || msg[0] != "vmmsg") { return; } - const topics = msg[3].split('|'); + const topics = msg[3].split("|"); const topic = topics[0]; - if (this.eventHandlers && this.eventHandlers[topic] != undefined) { - this.eventHandlers[topic](msg.slice(1)); + if (this.eventHandlers && this.eventHandlers.has(topic)) { + const eventHandler = this.eventHandlers.get(topic)!; + const eventHandlerMsg = msg.slice(1); + eventHandler(eventHandlerMsg); } } -} \ No newline at end of file +} diff --git a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts index 658c412262..752d0c8c55 100644 --- a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts @@ -1,25 +1,29 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import * as wasmclient from "./index" -import {Configuration, IConfiguration} from "./configuration" +import * as wasmclient from "./index"; +import { Configuration, IConfiguration } from "./configuration"; +import { AccountsService } from "./core_services/accounts_service"; export class ServiceClient { waspClient: wasmclient.WaspClient; + goShimmerClient: wasmclient.GoShimmerClient; configuration: Configuration; - constructor(config: Configuration) { - this.configuration = config; - this.waspClient = new wasmclient.WaspClient(config.waspApiUrl, config.goShimmerApiUrl); + constructor(configuration: Configuration) { + this.configuration = configuration; + this.waspClient = new wasmclient.WaspClient(configuration.waspApiUrl); + const accountsService = new AccountsService(this); + this.goShimmerClient = new wasmclient.GoShimmerClient(configuration, accountsService); } static default(): ServiceClient { //TODO use TCP instead of websocket for event listener? - const defaultConfiguration : IConfiguration = { + const defaultConfiguration: IConfiguration = { seed: null, waspWebSocketUrl: "ws://127.0.0.1:9090", waspApiUrl: "127.0.0.1:9090", - goShimmerApiUrl: "127.0.0.1:8080" + goShimmerApiUrl: "", }; return new ServiceClient(new Configuration(defaultConfiguration)); // "127.0.0.1:5550"); } diff --git a/packages/vm/wasmlib/ts/wasmclient/transfer.ts b/packages/vm/wasmlib/ts/wasmclient/transfer.ts index b082d0173c..660e80a979 100644 --- a/packages/vm/wasmlib/ts/wasmclient/transfer.ts +++ b/packages/vm/wasmlib/ts/wasmclient/transfer.ts @@ -4,6 +4,7 @@ import * as wasmclient from "./index" import {Base58} from "./crypto"; import {Buffer} from "./buffer"; +import { Colors } from "./colors"; export class Transfer { private xfer = new Map(); @@ -25,6 +26,12 @@ export class Transfer { this.xfer.set(color, amount); } + get(color: string): wasmclient.Uint64 { + if (color == Colors.IOTA_COLOR) color = Colors.IOTA_COLOR_STRING; + const amount = this.xfer.get(color) ?? 0n; + return amount; + } + // Encode returns a byte array that encodes the Transfer as follows: // Sort all nonzero transfers in ascending color order (very important, // because this data will be part of the data that will be signed, diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 4fe159e26b..822d0e59e0 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -1,58 +1,34 @@ // Copyright 2020 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import * as wasmclient from "./index" -import {Buffer} from "./buffer"; - -const headers: { [id: string]: string } = { - "Content-Type": "application/json", -}; - -interface IResponse { - error?: string; -} - -interface IExtendedResponse { - body: U; - response: Response; -} +import * as wasmclient from "./index"; +import { Buffer } from "./buffer"; +import { IResponse } from "./api_common/response_models"; +import * as requestSender from "./api_common/request_sender"; +import { Base58, ED25519, Hash, IKeyPair } from "./crypto"; interface ICallViewResponse extends IResponse { Items: [{ Key: string; Value: string }]; } -export interface ISendTransactionRequest { - txn_bytes: string; -} - -export interface ISendTransactionResponse extends IResponse { - transaction_id?: string; -} - interface IOffLedgerRequest { Request: string; } export class WaspClient { private waspAPI: string; - private goshimmerAPI: string; - constructor(waspAPI: string, goshimmerAPI: string) { - this.waspAPI = waspAPI; - if (!waspAPI.startsWith("http")) { - this.waspAPI = "http://" + waspAPI; - } - this.goshimmerAPI = goshimmerAPI; - if (!goshimmerAPI.startsWith("http")) { - this.goshimmerAPI = "http://" + goshimmerAPI; - } + constructor(waspAPI: string) { + if (waspAPI.startsWith("https://") || waspAPI.startsWith("http://")) this.waspAPI = waspAPI; + else this.waspAPI = "http://" + waspAPI; } public async callView(chainID: string, contractHName: string, entryPoint: string, args: Buffer): Promise { - const request = {Request: args.toString("base64")}; - const result = await this.sendRequest( + const request = { Request: args.toString("base64") }; + const result = await requestSender.sendRequestExt( + this.waspAPI, "post", - this.waspAPI + `/chain/${chainID}/contract/${contractHName}/callview/${entryPoint}`, + `/chain/${chainID}/contract/${contractHName}/callview/${entryPoint}`, request ); const res = new wasmclient.Results(); @@ -68,64 +44,39 @@ export class WaspClient { } public async postRequest(chainID: string, offLedgerRequest: Buffer): Promise { - const request = {Request: offLedgerRequest.toString("base64")}; - await this.sendRequest( - "post", - this.waspAPI + `/request/${chainID}`, - request, - ); - } - - public async postOnLedgerRequest(chainID: string, onLedgerRequest: Buffer): Promise { - const request = {txn_bytes: onLedgerRequest.toString("base64")}; - const response = await this.sendRequest( - "post", - this.goshimmerAPI + `/ledgerstate/transactions`, - request, - ); - return response.body; + const request = { Request: offLedgerRequest.toString("base64") }; + await requestSender.sendRequestExt(this.waspAPI, "post", `/request/${chainID}`, request); } public async waitRequest(chainID: string, reqID: wasmclient.RequestID): Promise { - await this.sendRequest( - "get", - this.waspAPI + `/chain/${chainID}/request/${reqID}/wait`, - ); + await requestSender.sendRequestExt(this.waspAPI, "get", `/chain/${chainID}/request/${reqID}/wait`); } - private async sendRequest( - verb: "put" | "post" | "get" | "delete", - path: string, - request?: T | undefined, - ): Promise> { - let response: U | null = null; - let fetchResponse: Response; - - try { - fetchResponse = await fetch(path, { - method: verb, - headers, - body: JSON.stringify(request), - }); - - if (!fetchResponse) { - throw new Error("No data was returned from the API"); - } - - try { - response = await fetchResponse.json(); - } catch (err) { - const error = err as Error; - if (!fetchResponse.ok) { - const text = await fetchResponse.text(); - throw new Error(error.message + " --- " + text); - } - } - } catch (err) { - const error = err as Error; - throw new Error("sendRequest: " + error.message); - } - - return {body: response, response: fetchResponse}; + public async postOffLedgerRequest( + chainId: string, + scHName: wasmclient.Hname, + hFuncName: wasmclient.Int32, + args: wasmclient.Arguments, + transfer: wasmclient.Transfer, + keyPair: IKeyPair + ): Promise { + // get request essence ready for signing + let essence = Base58.decode(chainId); + const hNames = Buffer.alloc(8); + hNames.writeUInt32LE(scHName, 0); + hNames.writeUInt32LE(hFuncName, 4); + const nonce = Buffer.alloc(8); + nonce.writeBigUInt64LE(BigInt(Math.trunc(performance.now())), 0); + essence = Buffer.concat([essence, hNames, args.encode(), keyPair.publicKey, nonce, transfer.encode()]); + + let buf = Buffer.alloc(1); + const requestTypeOffledger = 1; + buf.writeUInt8(requestTypeOffledger, 0); + buf = Buffer.concat([buf, essence, ED25519.privateSign(keyPair, essence)]); + const hash = Hash.from(buf); + const requestID = Buffer.concat([hash, Buffer.alloc(2)]); + await this.postRequest(chainId, buf); + + return Base58.encode(requestID); } } From 600a57be35781994a2b55bed3d089340de6ff335 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 6 Jan 2022 14:57:02 -0800 Subject: [PATCH 061/120] Added optional tokenURI parameter to erc721.mint() func --- contracts/wasm/erc721/go/erc721/consts.go | 2 + contracts/wasm/erc721/go/erc721/erc721.go | 13 +++++- contracts/wasm/erc721/go/erc721/keys.go | 32 +++++++------ contracts/wasm/erc721/go/erc721/params.go | 8 ++++ contracts/wasm/erc721/go/erc721/state.go | 30 +++++++++++++ contracts/wasm/erc721/schema.yaml | 2 + contracts/wasm/erc721/src/consts.rs | 22 ++++----- contracts/wasm/erc721/src/erc721.rs | 13 +++++- contracts/wasm/erc721/src/keys.rs | 52 ++++++++++++---------- contracts/wasm/erc721/src/params.rs | 8 ++++ contracts/wasm/erc721/src/state.rs | 36 +++++++++++++++ contracts/wasm/erc721/test/erc721_bg.wasm | Bin 39777 -> 40104 bytes contracts/wasm/erc721/ts/erc721/consts.ts | 2 + contracts/wasm/erc721/ts/erc721/erc721.ts | 13 +++++- contracts/wasm/erc721/ts/erc721/keys.ts | 30 +++++++------ contracts/wasm/erc721/ts/erc721/params.ts | 8 ++++ contracts/wasm/erc721/ts/erc721/state.ts | 38 ++++++++++++++++ 17 files changed, 245 insertions(+), 64 deletions(-) diff --git a/contracts/wasm/erc721/go/erc721/consts.go b/contracts/wasm/erc721/go/erc721/consts.go index f6d90db059..a40a421910 100644 --- a/contracts/wasm/erc721/go/erc721/consts.go +++ b/contracts/wasm/erc721/go/erc721/consts.go @@ -26,6 +26,7 @@ const ( ParamSymbol = "s" ParamTo = "to" ParamTokenID = "tokenID" + ParamTokenURI = "tokenURI" ) const ( @@ -45,6 +46,7 @@ const ( StateName = "name" StateOwners = "owners" StateSymbol = "symbol" + StateTokenURIs = "tokenURIs" ) const ( diff --git a/contracts/wasm/erc721/go/erc721/erc721.go b/contracts/wasm/erc721/go/erc721/erc721.go index c77513c27e..81b8cf78f1 100644 --- a/contracts/wasm/erc721/go/erc721/erc721.go +++ b/contracts/wasm/erc721/go/erc721/erc721.go @@ -141,6 +141,12 @@ func funcMint(ctx wasmlib.ScFuncContext, f *MintContext) { tokenOwner := f.State.Owners().GetAgentID(tokenID) ctx.Require(!tokenOwner.Exists(), "tokenID already minted") + // save optional token uri + tokenURI := f.Params.TokenURI() + if tokenURI.Exists() { + f.State.TokenURIs().GetString(tokenID).SetValue(tokenURI.Value()) + } + owner := ctx.Caller() tokenOwner.SetValue(owner) balance := f.State.Balances().GetUint64(owner) @@ -239,6 +245,11 @@ func viewSymbol(ctx wasmlib.ScViewContext, f *SymbolContext) { func viewTokenURI(ctx wasmlib.ScViewContext, f *TokenURIContext) { tokenID := f.Params.TokenID() if tokenID.Exists() { - f.Results.TokenURI().SetValue(baseURI + tokenID.String()) + tokenURI := baseURI + tokenID.String() + savedURI := f.State.TokenURIs().GetString(tokenID.Value()) + if savedURI.Exists() { + tokenURI = savedURI.Value() + } + f.Results.TokenURI().SetValue(tokenURI) } } diff --git a/contracts/wasm/erc721/go/erc721/keys.go b/contracts/wasm/erc721/go/erc721/keys.go index e9a054932e..a1f47ba9f4 100644 --- a/contracts/wasm/erc721/go/erc721/keys.go +++ b/contracts/wasm/erc721/go/erc721/keys.go @@ -20,24 +20,26 @@ const ( IdxParamSymbol = 7 IdxParamTo = 8 IdxParamTokenID = 9 + IdxParamTokenURI = 10 - IdxResultAmount = 10 - IdxResultApproval = 11 - IdxResultApproved = 12 - IdxResultName = 13 - IdxResultOwner = 14 - IdxResultSymbol = 15 - IdxResultTokenURI = 16 + IdxResultAmount = 11 + IdxResultApproval = 12 + IdxResultApproved = 13 + IdxResultName = 14 + IdxResultOwner = 15 + IdxResultSymbol = 16 + IdxResultTokenURI = 17 - IdxStateApprovedAccounts = 17 - IdxStateApprovedOperators = 18 - IdxStateBalances = 19 - IdxStateName = 20 - IdxStateOwners = 21 - IdxStateSymbol = 22 + IdxStateApprovedAccounts = 18 + IdxStateApprovedOperators = 19 + IdxStateBalances = 20 + IdxStateName = 21 + IdxStateOwners = 22 + IdxStateSymbol = 23 + IdxStateTokenURIs = 24 ) -const keyMapLen = 23 +const keyMapLen = 25 var keyMap = [keyMapLen]wasmlib.Key{ ParamApproval, @@ -50,6 +52,7 @@ var keyMap = [keyMapLen]wasmlib.Key{ ParamSymbol, ParamTo, ParamTokenID, + ParamTokenURI, ResultAmount, ResultApproval, ResultApproved, @@ -63,6 +66,7 @@ var keyMap = [keyMapLen]wasmlib.Key{ StateName, StateOwners, StateSymbol, + StateTokenURIs, } var idxMap [keyMapLen]wasmlib.Key32 diff --git a/contracts/wasm/erc721/go/erc721/params.go b/contracts/wasm/erc721/go/erc721/params.go index bc46fe5b82..63a30646d2 100644 --- a/contracts/wasm/erc721/go/erc721/params.go +++ b/contracts/wasm/erc721/go/erc721/params.go @@ -81,6 +81,10 @@ func (s ImmutableMintParams) TokenID() wasmlib.ScImmutableHash { return wasmlib.NewScImmutableHash(s.id, wasmlib.KeyID(ParamTokenID)) } +func (s ImmutableMintParams) TokenURI() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamTokenURI)) +} + type MutableMintParams struct { id int32 } @@ -89,6 +93,10 @@ func (s MutableMintParams) TokenID() wasmlib.ScMutableHash { return wasmlib.NewScMutableHash(s.id, wasmlib.KeyID(ParamTokenID)) } +func (s MutableMintParams) TokenURI() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamTokenURI)) +} + type ImmutableSafeTransferFromParams struct { id int32 } diff --git a/contracts/wasm/erc721/go/erc721/state.go b/contracts/wasm/erc721/go/erc721/state.go index d4877efb78..e2204b0108 100644 --- a/contracts/wasm/erc721/go/erc721/state.go +++ b/contracts/wasm/erc721/go/erc721/state.go @@ -34,6 +34,14 @@ func (m MapAgentIDToImmutableUint64) GetUint64(key wasmlib.ScAgentID) wasmlib.Sc return wasmlib.NewScImmutableUint64(m.objID, key.KeyID()) } +type MapHashToImmutableString struct { + objID int32 +} + +func (m MapHashToImmutableString) GetString(key wasmlib.ScHash) wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(m.objID, key.KeyID()) +} + type ImmutableErc721State struct { id int32 } @@ -66,6 +74,11 @@ func (s ImmutableErc721State) Symbol() wasmlib.ScImmutableString { return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(StateSymbol)) } +func (s ImmutableErc721State) TokenURIs() MapHashToImmutableString { + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateTokenURIs), wasmlib.TYPE_MAP) + return MapHashToImmutableString{objID: mapID} +} + type MapHashToMutableAgentID struct { objID int32 } @@ -103,6 +116,18 @@ func (m MapAgentIDToMutableUint64) GetUint64(key wasmlib.ScAgentID) wasmlib.ScMu return wasmlib.NewScMutableUint64(m.objID, key.KeyID()) } +type MapHashToMutableString struct { + objID int32 +} + +func (m MapHashToMutableString) Clear() { + wasmlib.Clear(m.objID) +} + +func (m MapHashToMutableString) GetString(key wasmlib.ScHash) wasmlib.ScMutableString { + return wasmlib.NewScMutableString(m.objID, key.KeyID()) +} + type MutableErc721State struct { id int32 } @@ -138,3 +163,8 @@ func (s MutableErc721State) Owners() MapHashToMutableAgentID { func (s MutableErc721State) Symbol() wasmlib.ScMutableString { return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(StateSymbol)) } + +func (s MutableErc721State) TokenURIs() MapHashToMutableString { + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateTokenURIs), wasmlib.TYPE_MAP) + return MapHashToMutableString{objID: mapID} +} diff --git a/contracts/wasm/erc721/schema.yaml b/contracts/wasm/erc721/schema.yaml index bbb5f0076e..5a96e6db04 100644 --- a/contracts/wasm/erc721/schema.yaml +++ b/contracts/wasm/erc721/schema.yaml @@ -30,6 +30,7 @@ state: approvedAccounts: map[Hash]AgentID // approved token controller agents balances: map[AgentID]Uint64 // number of tokens held by owners approvedOperators: map[AgentID]Operators // approved operators per owner + tokenURIs: map[Hash]String // token uri per token id funcs: approve: params: @@ -45,6 +46,7 @@ funcs: mint: params: tokenID: Hash // New token id + tokenURI: String? // Optional token URI that overrides default safeTransferFrom: params: from: AgentID // from account diff --git a/contracts/wasm/erc721/src/consts.rs b/contracts/wasm/erc721/src/consts.rs index 69c5bce108..61777ea8cc 100644 --- a/contracts/wasm/erc721/src/consts.rs +++ b/contracts/wasm/erc721/src/consts.rs @@ -13,16 +13,17 @@ pub const SC_NAME : &str = "erc721"; pub const SC_DESCRIPTION : &str = "ERC-721 NFT PoC for IOTA Smart Contracts"; pub const HSC_NAME : ScHname = ScHname(0xd967c216); -pub const PARAM_APPROVAL : &str = "approval"; -pub const PARAM_APPROVED : &str = "approved"; -pub const PARAM_DATA : &str = "data"; -pub const PARAM_FROM : &str = "from"; -pub const PARAM_NAME : &str = "n"; -pub const PARAM_OPERATOR : &str = "operator"; -pub const PARAM_OWNER : &str = "owner"; -pub const PARAM_SYMBOL : &str = "s"; -pub const PARAM_TO : &str = "to"; -pub const PARAM_TOKEN_ID : &str = "tokenID"; +pub const PARAM_APPROVAL : &str = "approval"; +pub const PARAM_APPROVED : &str = "approved"; +pub const PARAM_DATA : &str = "data"; +pub const PARAM_FROM : &str = "from"; +pub const PARAM_NAME : &str = "n"; +pub const PARAM_OPERATOR : &str = "operator"; +pub const PARAM_OWNER : &str = "owner"; +pub const PARAM_SYMBOL : &str = "s"; +pub const PARAM_TO : &str = "to"; +pub const PARAM_TOKEN_ID : &str = "tokenID"; +pub const PARAM_TOKEN_URI : &str = "tokenURI"; pub const RESULT_AMOUNT : &str = "amount"; pub const RESULT_APPROVAL : &str = "approval"; @@ -38,6 +39,7 @@ pub const STATE_BALANCES : &str = "balances"; pub const STATE_NAME : &str = "name"; pub const STATE_OWNERS : &str = "owners"; pub const STATE_SYMBOL : &str = "symbol"; +pub const STATE_TOKEN_UR_IS : &str = "tokenURIs"; pub const FUNC_APPROVE : &str = "approve"; pub const FUNC_BURN : &str = "burn"; diff --git a/contracts/wasm/erc721/src/erc721.rs b/contracts/wasm/erc721/src/erc721.rs index c313ad4727..26b0263492 100644 --- a/contracts/wasm/erc721/src/erc721.rs +++ b/contracts/wasm/erc721/src/erc721.rs @@ -140,6 +140,12 @@ pub fn func_mint(ctx: &ScFuncContext, f: &MintContext) { let token_owner = f.state.owners().get_agent_id(&token_id); ctx.require(!token_owner.exists(), "tokenID already minted"); + // save optional token uri + let token_uri = f.params.token_uri(); + if token_uri.exists() { + f.state.token_ur_is().get_string(&token_id).set_value(&token_uri.value()); + } + let owner = ctx.caller(); token_owner.set_value(&owner); let balance = f.state.balances().get_uint64(&owner); @@ -238,6 +244,11 @@ pub fn view_symbol(_ctx: &ScViewContext, f: &SymbolContext) { pub fn view_token_uri(_ctx: &ScViewContext, f: &TokenURIContext) { let token_id = f.params.token_id(); if token_id.exists() { - f.results.token_uri().set_value(&(BASE_URI.to_owned() + &token_id.to_string())); + let mut token_uri = BASE_URI.to_owned() + &token_id.to_string(); + let saved_uri = f.state.token_ur_is().get_string(&token_id.value()); + if saved_uri.exists() { + token_uri = saved_uri.value(); + } + f.results.token_uri().set_value(&token_uri); } } diff --git a/contracts/wasm/erc721/src/keys.rs b/contracts/wasm/erc721/src/keys.rs index a806243367..dacf7d5ea9 100644 --- a/contracts/wasm/erc721/src/keys.rs +++ b/contracts/wasm/erc721/src/keys.rs @@ -11,33 +11,35 @@ use wasmlib::*; use crate::*; -pub(crate) const IDX_PARAM_APPROVAL : usize = 0; -pub(crate) const IDX_PARAM_APPROVED : usize = 1; -pub(crate) const IDX_PARAM_DATA : usize = 2; -pub(crate) const IDX_PARAM_FROM : usize = 3; -pub(crate) const IDX_PARAM_NAME : usize = 4; -pub(crate) const IDX_PARAM_OPERATOR : usize = 5; -pub(crate) const IDX_PARAM_OWNER : usize = 6; -pub(crate) const IDX_PARAM_SYMBOL : usize = 7; -pub(crate) const IDX_PARAM_TO : usize = 8; -pub(crate) const IDX_PARAM_TOKEN_ID : usize = 9; +pub(crate) const IDX_PARAM_APPROVAL : usize = 0; +pub(crate) const IDX_PARAM_APPROVED : usize = 1; +pub(crate) const IDX_PARAM_DATA : usize = 2; +pub(crate) const IDX_PARAM_FROM : usize = 3; +pub(crate) const IDX_PARAM_NAME : usize = 4; +pub(crate) const IDX_PARAM_OPERATOR : usize = 5; +pub(crate) const IDX_PARAM_OWNER : usize = 6; +pub(crate) const IDX_PARAM_SYMBOL : usize = 7; +pub(crate) const IDX_PARAM_TO : usize = 8; +pub(crate) const IDX_PARAM_TOKEN_ID : usize = 9; +pub(crate) const IDX_PARAM_TOKEN_URI : usize = 10; -pub(crate) const IDX_RESULT_AMOUNT : usize = 10; -pub(crate) const IDX_RESULT_APPROVAL : usize = 11; -pub(crate) const IDX_RESULT_APPROVED : usize = 12; -pub(crate) const IDX_RESULT_NAME : usize = 13; -pub(crate) const IDX_RESULT_OWNER : usize = 14; -pub(crate) const IDX_RESULT_SYMBOL : usize = 15; -pub(crate) const IDX_RESULT_TOKEN_URI : usize = 16; +pub(crate) const IDX_RESULT_AMOUNT : usize = 11; +pub(crate) const IDX_RESULT_APPROVAL : usize = 12; +pub(crate) const IDX_RESULT_APPROVED : usize = 13; +pub(crate) const IDX_RESULT_NAME : usize = 14; +pub(crate) const IDX_RESULT_OWNER : usize = 15; +pub(crate) const IDX_RESULT_SYMBOL : usize = 16; +pub(crate) const IDX_RESULT_TOKEN_URI : usize = 17; -pub(crate) const IDX_STATE_APPROVED_ACCOUNTS : usize = 17; -pub(crate) const IDX_STATE_APPROVED_OPERATORS : usize = 18; -pub(crate) const IDX_STATE_BALANCES : usize = 19; -pub(crate) const IDX_STATE_NAME : usize = 20; -pub(crate) const IDX_STATE_OWNERS : usize = 21; -pub(crate) const IDX_STATE_SYMBOL : usize = 22; +pub(crate) const IDX_STATE_APPROVED_ACCOUNTS : usize = 18; +pub(crate) const IDX_STATE_APPROVED_OPERATORS : usize = 19; +pub(crate) const IDX_STATE_BALANCES : usize = 20; +pub(crate) const IDX_STATE_NAME : usize = 21; +pub(crate) const IDX_STATE_OWNERS : usize = 22; +pub(crate) const IDX_STATE_SYMBOL : usize = 23; +pub(crate) const IDX_STATE_TOKEN_UR_IS : usize = 24; -pub const KEY_MAP_LEN: usize = 23; +pub const KEY_MAP_LEN: usize = 25; pub const KEY_MAP: [&str; KEY_MAP_LEN] = [ PARAM_APPROVAL, @@ -50,6 +52,7 @@ pub const KEY_MAP: [&str; KEY_MAP_LEN] = [ PARAM_SYMBOL, PARAM_TO, PARAM_TOKEN_ID, + PARAM_TOKEN_URI, RESULT_AMOUNT, RESULT_APPROVAL, RESULT_APPROVED, @@ -63,6 +66,7 @@ pub const KEY_MAP: [&str; KEY_MAP_LEN] = [ STATE_NAME, STATE_OWNERS, STATE_SYMBOL, + STATE_TOKEN_UR_IS, ]; pub static mut IDX_MAP: [Key32; KEY_MAP_LEN] = [Key32(0); KEY_MAP_LEN]; diff --git a/contracts/wasm/erc721/src/params.rs b/contracts/wasm/erc721/src/params.rs index 4658d99a38..14db52728e 100644 --- a/contracts/wasm/erc721/src/params.rs +++ b/contracts/wasm/erc721/src/params.rs @@ -106,6 +106,10 @@ impl ImmutableMintParams { pub fn token_id(&self) -> ScImmutableHash { ScImmutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } + + pub fn token_uri(&self) -> ScImmutableString { + ScImmutableString::new(self.id, PARAM_TOKEN_URI.get_key_id()) + } } #[derive(Clone, Copy)] @@ -117,6 +121,10 @@ impl MutableMintParams { pub fn token_id(&self) -> ScMutableHash { ScMutableHash::new(self.id, PARAM_TOKEN_ID.get_key_id()) } + + pub fn token_uri(&self) -> ScMutableString { + ScMutableString::new(self.id, PARAM_TOKEN_URI.get_key_id()) + } } #[derive(Clone, Copy)] diff --git a/contracts/wasm/erc721/src/state.rs b/contracts/wasm/erc721/src/state.rs index b438698ca5..48a77bd618 100644 --- a/contracts/wasm/erc721/src/state.rs +++ b/contracts/wasm/erc721/src/state.rs @@ -49,6 +49,17 @@ impl MapAgentIDToImmutableUint64 { } } +#[derive(Clone, Copy)] +pub struct MapHashToImmutableString { + pub(crate) obj_id: i32, +} + +impl MapHashToImmutableString { + pub fn get_string(&self, key: &ScHash) -> ScImmutableString { + ScImmutableString::new(self.obj_id, key.get_key_id()) + } +} + #[derive(Clone, Copy)] pub struct ImmutableErc721State { pub(crate) id: i32, @@ -82,6 +93,11 @@ impl ImmutableErc721State { pub fn symbol(&self) -> ScImmutableString { ScImmutableString::new(self.id, STATE_SYMBOL.get_key_id()) } + + pub fn token_ur_is(&self) -> MapHashToImmutableString { + let map_id = get_object_id(self.id, STATE_TOKEN_UR_IS.get_key_id(), TYPE_MAP); + MapHashToImmutableString { obj_id: map_id } + } } #[derive(Clone, Copy)] @@ -130,6 +146,21 @@ impl MapAgentIDToMutableUint64 { } } +#[derive(Clone, Copy)] +pub struct MapHashToMutableString { + pub(crate) obj_id: i32, +} + +impl MapHashToMutableString { + pub fn clear(&self) { + clear(self.obj_id); + } + + pub fn get_string(&self, key: &ScHash) -> ScMutableString { + ScMutableString::new(self.obj_id, key.get_key_id()) + } +} + #[derive(Clone, Copy)] pub struct MutableErc721State { pub(crate) id: i32, @@ -167,4 +198,9 @@ impl MutableErc721State { pub fn symbol(&self) -> ScMutableString { ScMutableString::new(self.id, STATE_SYMBOL.get_key_id()) } + + pub fn token_ur_is(&self) -> MapHashToMutableString { + let map_id = get_object_id(self.id, STATE_TOKEN_UR_IS.get_key_id(), TYPE_MAP); + MapHashToMutableString { obj_id: map_id } + } } diff --git a/contracts/wasm/erc721/test/erc721_bg.wasm b/contracts/wasm/erc721/test/erc721_bg.wasm index 3a7d05521ee3ff423ca25030176efe3b706c0316..5031bf39d07bf228234095b63a54e3d21d1f463f 100644 GIT binary patch delta 7506 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 delta 7243 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 diff --git a/contracts/wasm/erc721/ts/erc721/consts.ts b/contracts/wasm/erc721/ts/erc721/consts.ts index 443f42c825..e32306cfa0 100644 --- a/contracts/wasm/erc721/ts/erc721/consts.ts +++ b/contracts/wasm/erc721/ts/erc721/consts.ts @@ -21,6 +21,7 @@ export const ParamOwner = "owner"; export const ParamSymbol = "s"; export const ParamTo = "to"; export const ParamTokenID = "tokenID"; +export const ParamTokenURI = "tokenURI"; export const ResultAmount = "amount"; export const ResultApproval = "approval"; @@ -36,6 +37,7 @@ export const StateBalances = "balances"; export const StateName = "name"; export const StateOwners = "owners"; export const StateSymbol = "symbol"; +export const StateTokenURIs = "tokenURIs"; export const FuncApprove = "approve"; export const FuncBurn = "burn"; diff --git a/contracts/wasm/erc721/ts/erc721/erc721.ts b/contracts/wasm/erc721/ts/erc721/erc721.ts index 1cd4d73655..3a0612f769 100644 --- a/contracts/wasm/erc721/ts/erc721/erc721.ts +++ b/contracts/wasm/erc721/ts/erc721/erc721.ts @@ -139,6 +139,12 @@ export function funcMint(ctx: wasmlib.ScFuncContext, f: sc.MintContext): void { let tokenOwner = f.state.owners().getAgentID(tokenID); ctx.require(!tokenOwner.exists(), "tokenID already minted"); + // save optional token uri + let tokenURI = f.params.tokenURI(); + if (tokenURI.exists()) { + f.state.tokenURIs().getString(tokenID).setValue(tokenURI.value()); + } + let owner = ctx.caller(); tokenOwner.setValue(owner); let balance = f.state.balances().getUint64(owner); @@ -237,6 +243,11 @@ export function viewSymbol(ctx: wasmlib.ScViewContext, f: sc.SymbolContext): voi export function viewTokenURI(ctx: wasmlib.ScViewContext, f: sc.TokenURIContext): void { let tokenID = f.params.tokenID(); if (tokenID.exists()) { - f.results.tokenURI().setValue(BASE_URI + tokenID.toString()); + let tokenURI = BASE_URI + tokenID.toString(); + let savedURI = f.state.tokenURIs().getString(tokenID.value()); + if (savedURI.exists()) { + tokenURI = savedURI.value(); + } + f.results.tokenURI().setValue(tokenURI); } } diff --git a/contracts/wasm/erc721/ts/erc721/keys.ts b/contracts/wasm/erc721/ts/erc721/keys.ts index d7fc649be8..fc32f134a8 100644 --- a/contracts/wasm/erc721/ts/erc721/keys.ts +++ b/contracts/wasm/erc721/ts/erc721/keys.ts @@ -18,21 +18,23 @@ export const IdxParamOwner = 6; export const IdxParamSymbol = 7; export const IdxParamTo = 8; export const IdxParamTokenID = 9; +export const IdxParamTokenURI = 10; -export const IdxResultAmount = 10; -export const IdxResultApproval = 11; -export const IdxResultApproved = 12; -export const IdxResultName = 13; -export const IdxResultOwner = 14; -export const IdxResultSymbol = 15; -export const IdxResultTokenURI = 16; +export const IdxResultAmount = 11; +export const IdxResultApproval = 12; +export const IdxResultApproved = 13; +export const IdxResultName = 14; +export const IdxResultOwner = 15; +export const IdxResultSymbol = 16; +export const IdxResultTokenURI = 17; -export const IdxStateApprovedAccounts = 17; -export const IdxStateApprovedOperators = 18; -export const IdxStateBalances = 19; -export const IdxStateName = 20; -export const IdxStateOwners = 21; -export const IdxStateSymbol = 22; +export const IdxStateApprovedAccounts = 18; +export const IdxStateApprovedOperators = 19; +export const IdxStateBalances = 20; +export const IdxStateName = 21; +export const IdxStateOwners = 22; +export const IdxStateSymbol = 23; +export const IdxStateTokenURIs = 24; export let keyMap: string[] = [ sc.ParamApproval, @@ -45,6 +47,7 @@ export let keyMap: string[] = [ sc.ParamSymbol, sc.ParamTo, sc.ParamTokenID, + sc.ParamTokenURI, sc.ResultAmount, sc.ResultApproval, sc.ResultApproved, @@ -58,6 +61,7 @@ export let keyMap: string[] = [ sc.StateName, sc.StateOwners, sc.StateSymbol, + sc.StateTokenURIs, ]; export let idxMap: wasmlib.Key32[] = new Array(keyMap.length); diff --git a/contracts/wasm/erc721/ts/erc721/params.ts b/contracts/wasm/erc721/ts/erc721/params.ts index 23aff4200b..509be4795f 100644 --- a/contracts/wasm/erc721/ts/erc721/params.ts +++ b/contracts/wasm/erc721/ts/erc721/params.ts @@ -64,12 +64,20 @@ export class ImmutableMintParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScImmutableHash { return new wasmlib.ScImmutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } + + tokenURI(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenURI)); + } } export class MutableMintParams extends wasmlib.ScMapID { tokenID(): wasmlib.ScMutableHash { return new wasmlib.ScMutableHash(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenID)); } + + tokenURI(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamTokenURI)); + } } export class ImmutableSafeTransferFromParams extends wasmlib.ScMapID { diff --git a/contracts/wasm/erc721/ts/erc721/state.ts b/contracts/wasm/erc721/ts/erc721/state.ts index 989fce026a..b16ed14851 100644 --- a/contracts/wasm/erc721/ts/erc721/state.ts +++ b/contracts/wasm/erc721/ts/erc721/state.ts @@ -45,6 +45,18 @@ export class MapAgentIDToImmutableUint64 { } } +export class MapHashToImmutableString { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getString(key: wasmlib.ScHash): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.objID, key.getKeyID()); + } +} + export class ImmutableErc721State extends wasmlib.ScMapID { approvedAccounts(): sc.MapHashToImmutableAgentID { let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateApprovedAccounts), wasmlib.TYPE_MAP); @@ -73,6 +85,11 @@ export class ImmutableErc721State extends wasmlib.ScMapID { symbol(): wasmlib.ScImmutableString { return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.StateSymbol)); } + + tokenURIs(): sc.MapHashToImmutableString { + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateTokenURIs), wasmlib.TYPE_MAP); + return new sc.MapHashToImmutableString(mapID); + } } export class MapHashToMutableAgentID { @@ -124,6 +141,22 @@ export class MapAgentIDToMutableUint64 { } } +export class MapHashToMutableString { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + getString(key: wasmlib.ScHash): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.objID, key.getKeyID()); + } +} + export class MutableErc721State extends wasmlib.ScMapID { asImmutable(): sc.ImmutableErc721State { const imm = new sc.ImmutableErc721State(); @@ -158,4 +191,9 @@ export class MutableErc721State extends wasmlib.ScMapID { symbol(): wasmlib.ScMutableString { return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.StateSymbol)); } + + tokenURIs(): sc.MapHashToMutableString { + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateTokenURIs), wasmlib.TYPE_MAP); + return new sc.MapHashToMutableString(mapID); + } } From 452ffe8f4ff2216e95e388373c4d2787c5a7b1d2 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 6 Jan 2022 17:18:23 -0800 Subject: [PATCH 062/120] Generate client interface for core contracts --- contracts/wasm/core_build.cmd | 2 +- .../go/wasmclient/coreaccounts/service.go | 192 +++++++++ .../wasmlib/go/wasmclient/coreblob/service.go | 137 +++++++ .../go/wasmclient/coreblocklog/service.go | 346 ++++++++++++++++ .../go/wasmclient/coregovernance/service.go | 374 ++++++++++++++++++ .../wasmlib/go/wasmclient/coreroot/service.go | 159 ++++++++ packages/vm/wasmlib/go/wasmclient/service.go | 11 +- .../core_services/accounts_service.ts | 28 -- .../ts/wasmclient/coreaccounts/service.ts | 170 ++++++++ .../wasmlib/ts/wasmclient/coreblob/service.ts | 127 ++++++ .../ts/wasmclient/coreblocklog/service.ts | 309 +++++++++++++++ .../ts/wasmclient/coregovernance/service.ts | 347 ++++++++++++++++ .../wasmlib/ts/wasmclient/coreroot/service.ts | 143 +++++++ packages/vm/wasmlib/ts/wasmclient/service.ts | 7 + tools/schema/generator/clientbase.go | 12 +- tools/schema/generator/generator_tsclient.go | 3 + .../generator/goclienttemplates/service.go | 12 +- .../tsclienttemplates/alltemplates.go | 12 + .../generator/tsclienttemplates/events.go | 2 +- .../generator/tsclienttemplates/funcs.go | 4 +- .../generator/tsclienttemplates/service.go | 12 +- 21 files changed, 2367 insertions(+), 42 deletions(-) create mode 100644 packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go create mode 100644 packages/vm/wasmlib/go/wasmclient/coreblob/service.go create mode 100644 packages/vm/wasmlib/go/wasmclient/coreblocklog/service.go create mode 100644 packages/vm/wasmlib/go/wasmclient/coregovernance/service.go create mode 100644 packages/vm/wasmlib/go/wasmclient/coreroot/service.go delete mode 100644 packages/vm/wasmlib/ts/wasmclient/core_services/accounts_service.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts diff --git a/contracts/wasm/core_build.cmd b/contracts/wasm/core_build.cmd index 6db737fb30..b6c208ba50 100644 --- a/contracts/wasm/core_build.cmd +++ b/contracts/wasm/core_build.cmd @@ -1,6 +1,6 @@ @echo off cd ..\..\packages\vm\wasmlib -schema -core -go -rust -ts -force +schema -core -go -rust -ts -client -force del /s /q d:\work\node_modules\wasmlib\*.* >nul: del /s /q d:\work\node_modules\wasmclient\*.* >nul: xcopy /s /q d:\Work\go\github.com\iotaledger\wasp\packages\vm\wasmlib\ts\wasmclient d:\work\node_modules\wasmclient diff --git a/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go b/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go new file mode 100644 index 0000000000..957a391ee5 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go @@ -0,0 +1,192 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package coreaccountsclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +const ( + ArgAgentID = "a" + ArgWithdrawAmount = "m" + ArgWithdrawColor = "c" + + ResAccountNonce = "n" + ResAgents = "this" + ResBalances = "this" +) + +///////////////////////////// deposit ///////////////////////////// + +type DepositFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *DepositFunc) AgentID(v wasmclient.AgentID) { + f.args.SetAgentID(ArgAgentID, v) +} + +func (f *DepositFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0xbdc9102d, &f.args) +} + +///////////////////////////// harvest ///////////////////////////// + +type HarvestFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *HarvestFunc) WithdrawAmount(v int64) { + f.args.SetInt64(ArgWithdrawAmount, v) +} + +func (f *HarvestFunc) WithdrawColor(v wasmclient.Color) { + f.args.SetColor(ArgWithdrawColor, v) +} + +func (f *HarvestFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0x7b40efbd, &f.args) +} + +///////////////////////////// withdraw ///////////////////////////// + +type WithdrawFunc struct { + wasmclient.ClientFunc +} + +func (f *WithdrawFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0x9dcc0f41, nil) +} + +///////////////////////////// accounts ///////////////////////////// + +type AccountsView struct { + wasmclient.ClientView +} + +func (f *AccountsView) Call() AccountsResults { + f.ClientView.Call("accounts", nil) + return AccountsResults{res: f.Results()} +} + +type AccountsResults struct { + res wasmclient.Results +} + +func (r *AccountsResults) Agents() []byte { + return r.res.GetBytes(ResAgents) +} + +///////////////////////////// balance ///////////////////////////// + +type BalanceView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *BalanceView) AgentID(v wasmclient.AgentID) { + f.args.SetAgentID(ArgAgentID, v) +} + +func (f *BalanceView) Call() BalanceResults { + f.args.Mandatory(ArgAgentID) + f.ClientView.Call("balance", &f.args) + return BalanceResults{res: f.Results()} +} + +type BalanceResults struct { + res wasmclient.Results +} + +func (r *BalanceResults) Balances() int64 { + return r.res.GetInt64(ResBalances) +} + +///////////////////////////// getAccountNonce ///////////////////////////// + +type GetAccountNonceView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetAccountNonceView) AgentID(v wasmclient.AgentID) { + f.args.SetAgentID(ArgAgentID, v) +} + +func (f *GetAccountNonceView) Call() GetAccountNonceResults { + f.args.Mandatory(ArgAgentID) + f.ClientView.Call("getAccountNonce", &f.args) + return GetAccountNonceResults{res: f.Results()} +} + +type GetAccountNonceResults struct { + res wasmclient.Results +} + +func (r *GetAccountNonceResults) AccountNonce() int64 { + return r.res.GetInt64(ResAccountNonce) +} + +///////////////////////////// totalAssets ///////////////////////////// + +type TotalAssetsView struct { + wasmclient.ClientView +} + +func (f *TotalAssetsView) Call() TotalAssetsResults { + f.ClientView.Call("totalAssets", nil) + return TotalAssetsResults{res: f.Results()} +} + +type TotalAssetsResults struct { + res wasmclient.Results +} + +func (r *TotalAssetsResults) Balances() int64 { + return r.res.GetInt64(ResBalances) +} + +///////////////////////////// CoreAccountsService ///////////////////////////// + +type CoreAccountsService struct { + wasmclient.Service +} + +func NewCoreAccountsService(cl *wasmclient.ServiceClient, chainID string) (*CoreAccountsService, error) { + s := &CoreAccountsService{} + err := s.Service.Init(cl, chainID, 0x3c4b5e02, nil) + return s, err +} + +func (s *CoreAccountsService) Deposit() DepositFunc { + return DepositFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreAccountsService) Harvest() HarvestFunc { + return HarvestFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreAccountsService) Withdraw() WithdrawFunc { + return WithdrawFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreAccountsService) Accounts() AccountsView { + return AccountsView{ClientView: s.AsClientView()} +} + +func (s *CoreAccountsService) Balance() BalanceView { + return BalanceView{ClientView: s.AsClientView()} +} + +func (s *CoreAccountsService) GetAccountNonce() GetAccountNonceView { + return GetAccountNonceView{ClientView: s.AsClientView()} +} + +func (s *CoreAccountsService) TotalAssets() TotalAssetsView { + return TotalAssetsView{ClientView: s.AsClientView()} +} diff --git a/packages/vm/wasmlib/go/wasmclient/coreblob/service.go b/packages/vm/wasmlib/go/wasmclient/coreblob/service.go new file mode 100644 index 0000000000..24fa9d1236 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/coreblob/service.go @@ -0,0 +1,137 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package coreblobclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +const ( + ArgBlobs = "this" + ArgField = "field" + ArgHash = "hash" + + ResBlobSizes = "this" + ResBytes = "bytes" + ResHash = "hash" +) + +///////////////////////////// storeBlob ///////////////////////////// + +type StoreBlobFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *StoreBlobFunc) Blobs(v []byte) { + f.args.SetBytes(ArgBlobs, v) +} + +func (f *StoreBlobFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0xddd4c281, &f.args) +} + +///////////////////////////// getBlobField ///////////////////////////// + +type GetBlobFieldView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetBlobFieldView) Field(v string) { + f.args.SetString(ArgField, v) +} + +func (f *GetBlobFieldView) Hash(v wasmclient.Hash) { + f.args.SetHash(ArgHash, v) +} + +func (f *GetBlobFieldView) Call() GetBlobFieldResults { + f.args.Mandatory(ArgField) + f.args.Mandatory(ArgHash) + f.ClientView.Call("getBlobField", &f.args) + return GetBlobFieldResults{res: f.Results()} +} + +type GetBlobFieldResults struct { + res wasmclient.Results +} + +func (r *GetBlobFieldResults) Bytes() []byte { + return r.res.GetBytes(ResBytes) +} + +///////////////////////////// getBlobInfo ///////////////////////////// + +type GetBlobInfoView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetBlobInfoView) Hash(v wasmclient.Hash) { + f.args.SetHash(ArgHash, v) +} + +func (f *GetBlobInfoView) Call() GetBlobInfoResults { + f.args.Mandatory(ArgHash) + f.ClientView.Call("getBlobInfo", &f.args) + return GetBlobInfoResults{res: f.Results()} +} + +type GetBlobInfoResults struct { + res wasmclient.Results +} + +func (r *GetBlobInfoResults) BlobSizes() int32 { + return r.res.GetInt32(ResBlobSizes) +} + +///////////////////////////// listBlobs ///////////////////////////// + +type ListBlobsView struct { + wasmclient.ClientView +} + +func (f *ListBlobsView) Call() ListBlobsResults { + f.ClientView.Call("listBlobs", nil) + return ListBlobsResults{res: f.Results()} +} + +type ListBlobsResults struct { + res wasmclient.Results +} + +func (r *ListBlobsResults) BlobSizes() int32 { + return r.res.GetInt32(ResBlobSizes) +} + +///////////////////////////// CoreBlobService ///////////////////////////// + +type CoreBlobService struct { + wasmclient.Service +} + +func NewCoreBlobService(cl *wasmclient.ServiceClient, chainID string) (*CoreBlobService, error) { + s := &CoreBlobService{} + err := s.Service.Init(cl, chainID, 0xfd91bc63, nil) + return s, err +} + +func (s *CoreBlobService) StoreBlob() StoreBlobFunc { + return StoreBlobFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreBlobService) GetBlobField() GetBlobFieldView { + return GetBlobFieldView{ClientView: s.AsClientView()} +} + +func (s *CoreBlobService) GetBlobInfo() GetBlobInfoView { + return GetBlobInfoView{ClientView: s.AsClientView()} +} + +func (s *CoreBlobService) ListBlobs() ListBlobsView { + return ListBlobsView{ClientView: s.AsClientView()} +} diff --git a/packages/vm/wasmlib/go/wasmclient/coreblocklog/service.go b/packages/vm/wasmlib/go/wasmclient/coreblocklog/service.go new file mode 100644 index 0000000000..d270202d2b --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/coreblocklog/service.go @@ -0,0 +1,346 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package coreblocklogclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +const ( + ArgBlockIndex = "n" + ArgContractHname = "h" + ArgFromBlock = "f" + ArgRequestID = "u" + ArgToBlock = "t" + + ResBlockIndex = "n" + ResBlockInfo = "i" + ResEvent = "e" + ResGoverningAddress = "g" + ResRequestID = "u" + ResRequestIndex = "r" + ResRequestProcessed = "p" + ResRequestRecord = "d" + ResStateControllerAddress = "s" +) + +///////////////////////////// controlAddresses ///////////////////////////// + +type ControlAddressesView struct { + wasmclient.ClientView +} + +func (f *ControlAddressesView) Call() ControlAddressesResults { + f.ClientView.Call("controlAddresses", nil) + return ControlAddressesResults{res: f.Results()} +} + +type ControlAddressesResults struct { + res wasmclient.Results +} + +func (r *ControlAddressesResults) BlockIndex() int32 { + return r.res.GetInt32(ResBlockIndex) +} + +func (r *ControlAddressesResults) GoverningAddress() wasmclient.Address { + return r.res.GetAddress(ResGoverningAddress) +} + +func (r *ControlAddressesResults) StateControllerAddress() wasmclient.Address { + return r.res.GetAddress(ResStateControllerAddress) +} + +///////////////////////////// getBlockInfo ///////////////////////////// + +type GetBlockInfoView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetBlockInfoView) BlockIndex(v int32) { + f.args.SetInt32(ArgBlockIndex, v) +} + +func (f *GetBlockInfoView) Call() GetBlockInfoResults { + f.args.Mandatory(ArgBlockIndex) + f.ClientView.Call("getBlockInfo", &f.args) + return GetBlockInfoResults{res: f.Results()} +} + +type GetBlockInfoResults struct { + res wasmclient.Results +} + +func (r *GetBlockInfoResults) BlockInfo() []byte { + return r.res.GetBytes(ResBlockInfo) +} + +///////////////////////////// getEventsForBlock ///////////////////////////// + +type GetEventsForBlockView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetEventsForBlockView) BlockIndex(v int32) { + f.args.SetInt32(ArgBlockIndex, v) +} + +func (f *GetEventsForBlockView) Call() GetEventsForBlockResults { + f.args.Mandatory(ArgBlockIndex) + f.ClientView.Call("getEventsForBlock", &f.args) + return GetEventsForBlockResults{res: f.Results()} +} + +type GetEventsForBlockResults struct { + res wasmclient.Results +} + +func (r *GetEventsForBlockResults) Event() []byte { + return r.res.GetBytes(ResEvent) +} + +///////////////////////////// getEventsForContract ///////////////////////////// + +type GetEventsForContractView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetEventsForContractView) ContractHname(v wasmclient.Hname) { + f.args.SetHname(ArgContractHname, v) +} + +func (f *GetEventsForContractView) FromBlock(v int32) { + f.args.SetInt32(ArgFromBlock, v) +} + +func (f *GetEventsForContractView) ToBlock(v int32) { + f.args.SetInt32(ArgToBlock, v) +} + +func (f *GetEventsForContractView) Call() GetEventsForContractResults { + f.args.Mandatory(ArgContractHname) + f.ClientView.Call("getEventsForContract", &f.args) + return GetEventsForContractResults{res: f.Results()} +} + +type GetEventsForContractResults struct { + res wasmclient.Results +} + +func (r *GetEventsForContractResults) Event() []byte { + return r.res.GetBytes(ResEvent) +} + +///////////////////////////// getEventsForRequest ///////////////////////////// + +type GetEventsForRequestView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetEventsForRequestView) RequestID(v wasmclient.RequestID) { + f.args.SetRequestID(ArgRequestID, v) +} + +func (f *GetEventsForRequestView) Call() GetEventsForRequestResults { + f.args.Mandatory(ArgRequestID) + f.ClientView.Call("getEventsForRequest", &f.args) + return GetEventsForRequestResults{res: f.Results()} +} + +type GetEventsForRequestResults struct { + res wasmclient.Results +} + +func (r *GetEventsForRequestResults) Event() []byte { + return r.res.GetBytes(ResEvent) +} + +///////////////////////////// getLatestBlockInfo ///////////////////////////// + +type GetLatestBlockInfoView struct { + wasmclient.ClientView +} + +func (f *GetLatestBlockInfoView) Call() GetLatestBlockInfoResults { + f.ClientView.Call("getLatestBlockInfo", nil) + return GetLatestBlockInfoResults{res: f.Results()} +} + +type GetLatestBlockInfoResults struct { + res wasmclient.Results +} + +func (r *GetLatestBlockInfoResults) BlockIndex() int32 { + return r.res.GetInt32(ResBlockIndex) +} + +func (r *GetLatestBlockInfoResults) BlockInfo() []byte { + return r.res.GetBytes(ResBlockInfo) +} + +///////////////////////////// getRequestIDsForBlock ///////////////////////////// + +type GetRequestIDsForBlockView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetRequestIDsForBlockView) BlockIndex(v int32) { + f.args.SetInt32(ArgBlockIndex, v) +} + +func (f *GetRequestIDsForBlockView) Call() GetRequestIDsForBlockResults { + f.args.Mandatory(ArgBlockIndex) + f.ClientView.Call("getRequestIDsForBlock", &f.args) + return GetRequestIDsForBlockResults{res: f.Results()} +} + +type GetRequestIDsForBlockResults struct { + res wasmclient.Results +} + +func (r *GetRequestIDsForBlockResults) RequestID() wasmclient.RequestID { + return r.res.GetRequestID(ResRequestID) +} + +///////////////////////////// getRequestReceipt ///////////////////////////// + +type GetRequestReceiptView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetRequestReceiptView) RequestID(v wasmclient.RequestID) { + f.args.SetRequestID(ArgRequestID, v) +} + +func (f *GetRequestReceiptView) Call() GetRequestReceiptResults { + f.args.Mandatory(ArgRequestID) + f.ClientView.Call("getRequestReceipt", &f.args) + return GetRequestReceiptResults{res: f.Results()} +} + +type GetRequestReceiptResults struct { + res wasmclient.Results +} + +func (r *GetRequestReceiptResults) BlockIndex() int32 { + return r.res.GetInt32(ResBlockIndex) +} + +func (r *GetRequestReceiptResults) RequestIndex() int16 { + return r.res.GetInt16(ResRequestIndex) +} + +func (r *GetRequestReceiptResults) RequestRecord() []byte { + return r.res.GetBytes(ResRequestRecord) +} + +///////////////////////////// getRequestReceiptsForBlock ///////////////////////////// + +type GetRequestReceiptsForBlockView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetRequestReceiptsForBlockView) BlockIndex(v int32) { + f.args.SetInt32(ArgBlockIndex, v) +} + +func (f *GetRequestReceiptsForBlockView) Call() GetRequestReceiptsForBlockResults { + f.args.Mandatory(ArgBlockIndex) + f.ClientView.Call("getRequestReceiptsForBlock", &f.args) + return GetRequestReceiptsForBlockResults{res: f.Results()} +} + +type GetRequestReceiptsForBlockResults struct { + res wasmclient.Results +} + +func (r *GetRequestReceiptsForBlockResults) RequestRecord() []byte { + return r.res.GetBytes(ResRequestRecord) +} + +///////////////////////////// isRequestProcessed ///////////////////////////// + +type IsRequestProcessedView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *IsRequestProcessedView) RequestID(v wasmclient.RequestID) { + f.args.SetRequestID(ArgRequestID, v) +} + +func (f *IsRequestProcessedView) Call() IsRequestProcessedResults { + f.args.Mandatory(ArgRequestID) + f.ClientView.Call("isRequestProcessed", &f.args) + return IsRequestProcessedResults{res: f.Results()} +} + +type IsRequestProcessedResults struct { + res wasmclient.Results +} + +func (r *IsRequestProcessedResults) RequestProcessed() string { + return r.res.GetString(ResRequestProcessed) +} + +///////////////////////////// CoreBlockLogService ///////////////////////////// + +type CoreBlockLogService struct { + wasmclient.Service +} + +func NewCoreBlockLogService(cl *wasmclient.ServiceClient, chainID string) (*CoreBlockLogService, error) { + s := &CoreBlockLogService{} + err := s.Service.Init(cl, chainID, 0xf538ef2b, nil) + return s, err +} + +func (s *CoreBlockLogService) ControlAddresses() ControlAddressesView { + return ControlAddressesView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) GetBlockInfo() GetBlockInfoView { + return GetBlockInfoView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) GetEventsForBlock() GetEventsForBlockView { + return GetEventsForBlockView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) GetEventsForContract() GetEventsForContractView { + return GetEventsForContractView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) GetEventsForRequest() GetEventsForRequestView { + return GetEventsForRequestView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) GetLatestBlockInfo() GetLatestBlockInfoView { + return GetLatestBlockInfoView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) GetRequestIDsForBlock() GetRequestIDsForBlockView { + return GetRequestIDsForBlockView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) GetRequestReceipt() GetRequestReceiptView { + return GetRequestReceiptView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) GetRequestReceiptsForBlock() GetRequestReceiptsForBlockView { + return GetRequestReceiptsForBlockView{ClientView: s.AsClientView()} +} + +func (s *CoreBlockLogService) IsRequestProcessed() IsRequestProcessedView { + return IsRequestProcessedView{ClientView: s.AsClientView()} +} diff --git a/packages/vm/wasmlib/go/wasmclient/coregovernance/service.go b/packages/vm/wasmlib/go/wasmclient/coregovernance/service.go new file mode 100644 index 0000000000..3bb94cfb70 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/coregovernance/service.go @@ -0,0 +1,374 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package coregovernanceclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +const ( + ArgChainOwner = "oi" + ArgFeeColor = "fc" + ArgHname = "hn" + ArgMaxBlobSize = "bs" + ArgMaxEventSize = "es" + ArgMaxEventsPerReq = "ne" + ArgOwnerFee = "of" + ArgStateControllerAddress = "S" + ArgValidatorFee = "vf" + + ResAllowedStateControllerAddresses = "a" + ResChainID = "c" + ResChainOwnerID = "o" + ResDefaultOwnerFee = "do" + ResDefaultValidatorFee = "dv" + ResDescription = "d" + ResFeeColor = "f" + ResMaxBlobSize = "mb" + ResMaxEventSize = "me" + ResMaxEventsPerReq = "mr" + ResOwnerFee = "of" + ResValidatorFee = "vf" +) + +///////////////////////////// addAllowedStateControllerAddress ///////////////////////////// + +type AddAllowedStateControllerAddressFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *AddAllowedStateControllerAddressFunc) ChainOwner(v wasmclient.AgentID) { + f.args.SetAgentID(ArgChainOwner, v) +} + +func (f *AddAllowedStateControllerAddressFunc) FeeColor(v wasmclient.Color) { + f.args.SetColor(ArgFeeColor, v) +} + +func (f *AddAllowedStateControllerAddressFunc) StateControllerAddress(v wasmclient.Address) { + f.args.SetAddress(ArgStateControllerAddress, v) +} + +func (f *AddAllowedStateControllerAddressFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgChainOwner) + f.args.Mandatory(ArgStateControllerAddress) + return f.ClientFunc.Post(0x9469d567, &f.args) +} + +///////////////////////////// claimChainOwnership ///////////////////////////// + +type ClaimChainOwnershipFunc struct { + wasmclient.ClientFunc +} + +func (f *ClaimChainOwnershipFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0x03ff0fc0, nil) +} + +///////////////////////////// delegateChainOwnership ///////////////////////////// + +type DelegateChainOwnershipFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *DelegateChainOwnershipFunc) ChainOwner(v wasmclient.AgentID) { + f.args.SetAgentID(ArgChainOwner, v) +} + +func (f *DelegateChainOwnershipFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgChainOwner) + return f.ClientFunc.Post(0x93ecb6ad, &f.args) +} + +///////////////////////////// removeAllowedStateControllerAddress ///////////////////////////// + +type RemoveAllowedStateControllerAddressFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *RemoveAllowedStateControllerAddressFunc) StateControllerAddress(v wasmclient.Address) { + f.args.SetAddress(ArgStateControllerAddress, v) +} + +func (f *RemoveAllowedStateControllerAddressFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgStateControllerAddress) + return f.ClientFunc.Post(0x31f69447, &f.args) +} + +///////////////////////////// rotateStateController ///////////////////////////// + +type RotateStateControllerFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *RotateStateControllerFunc) StateControllerAddress(v wasmclient.Address) { + f.args.SetAddress(ArgStateControllerAddress, v) +} + +func (f *RotateStateControllerFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgStateControllerAddress) + return f.ClientFunc.Post(0x244d1038, &f.args) +} + +///////////////////////////// setChainInfo ///////////////////////////// + +type SetChainInfoFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *SetChainInfoFunc) MaxBlobSize(v int32) { + f.args.SetInt32(ArgMaxBlobSize, v) +} + +func (f *SetChainInfoFunc) MaxEventSize(v int16) { + f.args.SetInt16(ArgMaxEventSize, v) +} + +func (f *SetChainInfoFunc) MaxEventsPerReq(v int16) { + f.args.SetInt16(ArgMaxEventsPerReq, v) +} + +func (f *SetChainInfoFunc) OwnerFee(v int64) { + f.args.SetInt64(ArgOwnerFee, v) +} + +func (f *SetChainInfoFunc) ValidatorFee(v int64) { + f.args.SetInt64(ArgValidatorFee, v) +} + +func (f *SetChainInfoFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0x702f5d2b, &f.args) +} + +///////////////////////////// setContractFee ///////////////////////////// + +type SetContractFeeFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *SetContractFeeFunc) Hname(v wasmclient.Hname) { + f.args.SetHname(ArgHname, v) +} + +func (f *SetContractFeeFunc) OwnerFee(v int64) { + f.args.SetInt64(ArgOwnerFee, v) +} + +func (f *SetContractFeeFunc) ValidatorFee(v int64) { + f.args.SetInt64(ArgValidatorFee, v) +} + +func (f *SetContractFeeFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgHname) + return f.ClientFunc.Post(0x8421a42b, &f.args) +} + +///////////////////////////// setDefaultFee ///////////////////////////// + +type SetDefaultFeeFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *SetDefaultFeeFunc) OwnerFee(v int64) { + f.args.SetInt64(ArgOwnerFee, v) +} + +func (f *SetDefaultFeeFunc) ValidatorFee(v int64) { + f.args.SetInt64(ArgValidatorFee, v) +} + +func (f *SetDefaultFeeFunc) Post() wasmclient.Request { + return f.ClientFunc.Post(0x3310ecd0, &f.args) +} + +///////////////////////////// getAllowedStateControllerAddresses ///////////////////////////// + +type GetAllowedStateControllerAddressesView struct { + wasmclient.ClientView +} + +func (f *GetAllowedStateControllerAddressesView) Call() GetAllowedStateControllerAddressesResults { + f.ClientView.Call("getAllowedStateControllerAddresses", nil) + return GetAllowedStateControllerAddressesResults{res: f.Results()} +} + +type GetAllowedStateControllerAddressesResults struct { + res wasmclient.Results +} + +func (r *GetAllowedStateControllerAddressesResults) AllowedStateControllerAddresses() []byte { + return r.res.GetBytes(ResAllowedStateControllerAddresses) +} + +///////////////////////////// getChainInfo ///////////////////////////// + +type GetChainInfoView struct { + wasmclient.ClientView +} + +func (f *GetChainInfoView) Call() GetChainInfoResults { + f.ClientView.Call("getChainInfo", nil) + return GetChainInfoResults{res: f.Results()} +} + +type GetChainInfoResults struct { + res wasmclient.Results +} + +func (r *GetChainInfoResults) ChainID() wasmclient.ChainID { + return r.res.GetChainID(ResChainID) +} + +func (r *GetChainInfoResults) ChainOwnerID() wasmclient.AgentID { + return r.res.GetAgentID(ResChainOwnerID) +} + +func (r *GetChainInfoResults) DefaultOwnerFee() int64 { + return r.res.GetInt64(ResDefaultOwnerFee) +} + +func (r *GetChainInfoResults) DefaultValidatorFee() int64 { + return r.res.GetInt64(ResDefaultValidatorFee) +} + +func (r *GetChainInfoResults) Description() string { + return r.res.GetString(ResDescription) +} + +func (r *GetChainInfoResults) FeeColor() wasmclient.Color { + return r.res.GetColor(ResFeeColor) +} + +func (r *GetChainInfoResults) MaxBlobSize() int32 { + return r.res.GetInt32(ResMaxBlobSize) +} + +func (r *GetChainInfoResults) MaxEventSize() int16 { + return r.res.GetInt16(ResMaxEventSize) +} + +func (r *GetChainInfoResults) MaxEventsPerReq() int16 { + return r.res.GetInt16(ResMaxEventsPerReq) +} + +///////////////////////////// getFeeInfo ///////////////////////////// + +type GetFeeInfoView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *GetFeeInfoView) Hname(v wasmclient.Hname) { + f.args.SetHname(ArgHname, v) +} + +func (f *GetFeeInfoView) Call() GetFeeInfoResults { + f.args.Mandatory(ArgHname) + f.ClientView.Call("getFeeInfo", &f.args) + return GetFeeInfoResults{res: f.Results()} +} + +type GetFeeInfoResults struct { + res wasmclient.Results +} + +func (r *GetFeeInfoResults) FeeColor() wasmclient.Color { + return r.res.GetColor(ResFeeColor) +} + +func (r *GetFeeInfoResults) OwnerFee() int64 { + return r.res.GetInt64(ResOwnerFee) +} + +func (r *GetFeeInfoResults) ValidatorFee() int64 { + return r.res.GetInt64(ResValidatorFee) +} + +///////////////////////////// getMaxBlobSize ///////////////////////////// + +type GetMaxBlobSizeView struct { + wasmclient.ClientView +} + +func (f *GetMaxBlobSizeView) Call() GetMaxBlobSizeResults { + f.ClientView.Call("getMaxBlobSize", nil) + return GetMaxBlobSizeResults{res: f.Results()} +} + +type GetMaxBlobSizeResults struct { + res wasmclient.Results +} + +func (r *GetMaxBlobSizeResults) MaxBlobSize() int32 { + return r.res.GetInt32(ResMaxBlobSize) +} + +///////////////////////////// CoreGovernanceService ///////////////////////////// + +type CoreGovernanceService struct { + wasmclient.Service +} + +func NewCoreGovernanceService(cl *wasmclient.ServiceClient, chainID string) (*CoreGovernanceService, error) { + s := &CoreGovernanceService{} + err := s.Service.Init(cl, chainID, 0x17cf909f, nil) + return s, err +} + +func (s *CoreGovernanceService) AddAllowedStateControllerAddress() AddAllowedStateControllerAddressFunc { + return AddAllowedStateControllerAddressFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreGovernanceService) ClaimChainOwnership() ClaimChainOwnershipFunc { + return ClaimChainOwnershipFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreGovernanceService) DelegateChainOwnership() DelegateChainOwnershipFunc { + return DelegateChainOwnershipFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreGovernanceService) RemoveAllowedStateControllerAddress() RemoveAllowedStateControllerAddressFunc { + return RemoveAllowedStateControllerAddressFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreGovernanceService) RotateStateController() RotateStateControllerFunc { + return RotateStateControllerFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreGovernanceService) SetChainInfo() SetChainInfoFunc { + return SetChainInfoFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreGovernanceService) SetContractFee() SetContractFeeFunc { + return SetContractFeeFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreGovernanceService) SetDefaultFee() SetDefaultFeeFunc { + return SetDefaultFeeFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreGovernanceService) GetAllowedStateControllerAddresses() GetAllowedStateControllerAddressesView { + return GetAllowedStateControllerAddressesView{ClientView: s.AsClientView()} +} + +func (s *CoreGovernanceService) GetChainInfo() GetChainInfoView { + return GetChainInfoView{ClientView: s.AsClientView()} +} + +func (s *CoreGovernanceService) GetFeeInfo() GetFeeInfoView { + return GetFeeInfoView{ClientView: s.AsClientView()} +} + +func (s *CoreGovernanceService) GetMaxBlobSize() GetMaxBlobSizeView { + return GetMaxBlobSizeView{ClientView: s.AsClientView()} +} diff --git a/packages/vm/wasmlib/go/wasmclient/coreroot/service.go b/packages/vm/wasmlib/go/wasmclient/coreroot/service.go new file mode 100644 index 0000000000..36fa99205a --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/coreroot/service.go @@ -0,0 +1,159 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +package corerootclient + +import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + +const ( + ArgDeployer = "dp" + ArgDescription = "ds" + ArgHname = "hn" + ArgName = "nm" + ArgProgramHash = "ph" + + ResContractFound = "cf" + ResContractRecData = "dt" + ResContractRegistry = "r" +) + +///////////////////////////// deployContract ///////////////////////////// + +type DeployContractFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *DeployContractFunc) Description(v string) { + f.args.SetString(ArgDescription, v) +} + +func (f *DeployContractFunc) Name(v string) { + f.args.SetString(ArgName, v) +} + +func (f *DeployContractFunc) ProgramHash(v wasmclient.Hash) { + f.args.SetHash(ArgProgramHash, v) +} + +func (f *DeployContractFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgName) + f.args.Mandatory(ArgProgramHash) + return f.ClientFunc.Post(0x28232c27, &f.args) +} + +///////////////////////////// grantDeployPermission ///////////////////////////// + +type GrantDeployPermissionFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *GrantDeployPermissionFunc) Deployer(v wasmclient.AgentID) { + f.args.SetAgentID(ArgDeployer, v) +} + +func (f *GrantDeployPermissionFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgDeployer) + return f.ClientFunc.Post(0xf440263a, &f.args) +} + +///////////////////////////// revokeDeployPermission ///////////////////////////// + +type RevokeDeployPermissionFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *RevokeDeployPermissionFunc) Deployer(v wasmclient.AgentID) { + f.args.SetAgentID(ArgDeployer, v) +} + +func (f *RevokeDeployPermissionFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgDeployer) + return f.ClientFunc.Post(0x850744f1, &f.args) +} + +///////////////////////////// findContract ///////////////////////////// + +type FindContractView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *FindContractView) Hname(v wasmclient.Hname) { + f.args.SetHname(ArgHname, v) +} + +func (f *FindContractView) Call() FindContractResults { + f.args.Mandatory(ArgHname) + f.ClientView.Call("findContract", &f.args) + return FindContractResults{res: f.Results()} +} + +type FindContractResults struct { + res wasmclient.Results +} + +func (r *FindContractResults) ContractFound() []byte { + return r.res.GetBytes(ResContractFound) +} + +func (r *FindContractResults) ContractRecData() []byte { + return r.res.GetBytes(ResContractRecData) +} + +///////////////////////////// getContractRecords ///////////////////////////// + +type GetContractRecordsView struct { + wasmclient.ClientView +} + +func (f *GetContractRecordsView) Call() GetContractRecordsResults { + f.ClientView.Call("getContractRecords", nil) + return GetContractRecordsResults{res: f.Results()} +} + +type GetContractRecordsResults struct { + res wasmclient.Results +} + +func (r *GetContractRecordsResults) ContractRegistry() []byte { + return r.res.GetBytes(ResContractRegistry) +} + +///////////////////////////// CoreRootService ///////////////////////////// + +type CoreRootService struct { + wasmclient.Service +} + +func NewCoreRootService(cl *wasmclient.ServiceClient, chainID string) (*CoreRootService, error) { + s := &CoreRootService{} + err := s.Service.Init(cl, chainID, 0xcebf5908, nil) + return s, err +} + +func (s *CoreRootService) DeployContract() DeployContractFunc { + return DeployContractFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreRootService) GrantDeployPermission() GrantDeployPermissionFunc { + return GrantDeployPermissionFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreRootService) RevokeDeployPermission() RevokeDeployPermissionFunc { + return RevokeDeployPermissionFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *CoreRootService) FindContract() FindContractView { + return FindContractView{ClientView: s.AsClientView()} +} + +func (s *CoreRootService) GetContractRecords() GetContractRecordsView { + return GetContractRecordsView{ClientView: s.AsClientView()} +} diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go index d870a6934a..d9eeb69817 100644 --- a/packages/vm/wasmlib/go/wasmclient/service.go +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -33,7 +33,11 @@ func (s *Service) Init(svcClient *ServiceClient, chainID string, scHname uint32, if err != nil { return err } - return s.startEventHandlers(svcClient.eventPort, eventHandlers) + if len(eventHandlers) != 0 { + // TODO allow user to specify event handlers for core contracts? + return s.startEventHandlers(svcClient.eventPort, eventHandlers) + } + return nil } func (s *Service) AsClientFunc() ClientFunc { @@ -71,6 +75,11 @@ func (s *Service) PostRequest(hFuncName uint32, args *Arguments, transfer *Trans return Request{id: &id} } +// overrides default contract name +func (s *Service) ServiceContractName(contractName string) { + s.scHname = iscp.Hn(contractName) +} + func (s *Service) SignRequests(keyPair *ed25519.KeyPair) { s.keyPair = keyPair } diff --git a/packages/vm/wasmlib/ts/wasmclient/core_services/accounts_service.ts b/packages/vm/wasmlib/ts/wasmclient/core_services/accounts_service.ts deleted file mode 100644 index f27ec11488..0000000000 --- a/packages/vm/wasmlib/ts/wasmclient/core_services/accounts_service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Arguments, ClientFunc, RequestID } from ".."; -import { EventHandlers, Service } from "../service"; -import { ServiceClient } from "../serviceclient"; - -export class AccountsService extends Service { - public constructor(cl: ServiceClient) { - const emptyEventHandlers: EventHandlers = new Map(); - super(cl, 0x3c4b5e02, emptyEventHandlers); - } - - public deposit(): DepositFunc { - return new DepositFunc(this); - } -} - -export class DepositFunc extends ClientFunc { - private ArgAddress: string = "address"; - - private args: Arguments = new Arguments(); - - public address(v: string): void { - this.args.setString(this.ArgAddress, v); - } - - public async post(): Promise { - return await super.post(0xbdc9102d, this.args, false); - } -} diff --git a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts new file mode 100644 index 0000000000..0c7258d4de --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts @@ -0,0 +1,170 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmclient" + +const ArgAgentID = "a"; +const ArgWithdrawAmount = "m"; +const ArgWithdrawColor = "c"; + +const ResAccountNonce = "n"; +const ResAgents = "this"; +const ResBalances = "this"; + +///////////////////////////// deposit ///////////////////////////// + +export class DepositFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public agentID(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgAgentID, v); + } + + public async post(): Promise { + return await super.post(0xbdc9102d, this.args); + } +} + +///////////////////////////// harvest ///////////////////////////// + +export class HarvestFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public withdrawAmount(v: wasmclient.Int64): void { + this.args.setInt64(ArgWithdrawAmount, v); + } + + public withdrawColor(v: wasmclient.Color): void { + this.args.setColor(ArgWithdrawColor, v); + } + + public async post(): Promise { + return await super.post(0x7b40efbd, this.args); + } +} + +///////////////////////////// withdraw ///////////////////////////// + +export class WithdrawFunc extends wasmclient.ClientFunc { + + public async post(): Promise { + return await super.post(0x9dcc0f41, null); + } +} + +///////////////////////////// accounts ///////////////////////////// + +export class AccountsView extends wasmclient.ClientView { + + public async call(): Promise { + return new AccountsResults(await this.callView("accounts", null)); + } +} + +export class AccountsResults extends wasmclient.ViewResults { + + agents(): wasmclient.Bytes { + return this.res.getBytes(ResAgents); + } +} + +///////////////////////////// balance ///////////////////////////// + +export class BalanceView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public agentID(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgAgentID, v); + } + + public async call(): Promise { + this.args.mandatory(ArgAgentID); + return new BalanceResults(await this.callView("balance", this.args)); + } +} + +export class BalanceResults extends wasmclient.ViewResults { + + balances(): wasmclient.Int64 { + return this.res.getInt64(ResBalances); + } +} + +///////////////////////////// getAccountNonce ///////////////////////////// + +export class GetAccountNonceView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public agentID(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgAgentID, v); + } + + public async call(): Promise { + this.args.mandatory(ArgAgentID); + return new GetAccountNonceResults(await this.callView("getAccountNonce", this.args)); + } +} + +export class GetAccountNonceResults extends wasmclient.ViewResults { + + accountNonce(): wasmclient.Int64 { + return this.res.getInt64(ResAccountNonce); + } +} + +///////////////////////////// totalAssets ///////////////////////////// + +export class TotalAssetsView extends wasmclient.ClientView { + + public async call(): Promise { + return new TotalAssetsResults(await this.callView("totalAssets", null)); + } +} + +export class TotalAssetsResults extends wasmclient.ViewResults { + + balances(): wasmclient.Int64 { + return this.res.getInt64(ResBalances); + } +} + +///////////////////////////// CoreAccountsService ///////////////////////////// + +export class CoreAccountsService extends wasmclient.Service { + + public constructor(cl: wasmclient.ServiceClient) { + super(cl, 0x3c4b5e02, null); + } + + public deposit(): DepositFunc { + return new DepositFunc(this); + } + + public harvest(): HarvestFunc { + return new HarvestFunc(this); + } + + public withdraw(): WithdrawFunc { + return new WithdrawFunc(this); + } + + public accounts(): AccountsView { + return new AccountsView(this); + } + + public balance(): BalanceView { + return new BalanceView(this); + } + + public getAccountNonce(): GetAccountNonceView { + return new GetAccountNonceView(this); + } + + public totalAssets(): TotalAssetsView { + return new TotalAssetsView(this); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts new file mode 100644 index 0000000000..52ae9ceb13 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts @@ -0,0 +1,127 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmclient" + +const ArgBlobs = "this"; +const ArgField = "field"; +const ArgHash = "hash"; + +const ResBlobSizes = "this"; +const ResBytes = "bytes"; +const ResHash = "hash"; + +///////////////////////////// storeBlob ///////////////////////////// + +export class StoreBlobFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public blobs(v: wasmclient.Bytes): void { + this.args.setBytes(ArgBlobs, v); + } + + public async post(): Promise { + return await super.post(0xddd4c281, this.args); + } +} + +export class StoreBlobResults extends wasmclient.ViewResults { + + hash(): wasmclient.Hash { + return this.res.getHash(ResHash); + } +} + +///////////////////////////// getBlobField ///////////////////////////// + +export class GetBlobFieldView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public field(v: string): void { + this.args.setString(ArgField, v); + } + + public hash(v: wasmclient.Hash): void { + this.args.setHash(ArgHash, v); + } + + public async call(): Promise { + this.args.mandatory(ArgField); + this.args.mandatory(ArgHash); + return new GetBlobFieldResults(await this.callView("getBlobField", this.args)); + } +} + +export class GetBlobFieldResults extends wasmclient.ViewResults { + + bytes(): wasmclient.Bytes { + return this.res.getBytes(ResBytes); + } +} + +///////////////////////////// getBlobInfo ///////////////////////////// + +export class GetBlobInfoView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public hash(v: wasmclient.Hash): void { + this.args.setHash(ArgHash, v); + } + + public async call(): Promise { + this.args.mandatory(ArgHash); + return new GetBlobInfoResults(await this.callView("getBlobInfo", this.args)); + } +} + +export class GetBlobInfoResults extends wasmclient.ViewResults { + + blobSizes(): wasmclient.Int32 { + return this.res.getInt32(ResBlobSizes); + } +} + +///////////////////////////// listBlobs ///////////////////////////// + +export class ListBlobsView extends wasmclient.ClientView { + + public async call(): Promise { + return new ListBlobsResults(await this.callView("listBlobs", null)); + } +} + +export class ListBlobsResults extends wasmclient.ViewResults { + + blobSizes(): wasmclient.Int32 { + return this.res.getInt32(ResBlobSizes); + } +} + +///////////////////////////// CoreBlobService ///////////////////////////// + +export class CoreBlobService extends wasmclient.Service { + + public constructor(cl: wasmclient.ServiceClient) { + super(cl, 0xfd91bc63, null); + } + + public storeBlob(): StoreBlobFunc { + return new StoreBlobFunc(this); + } + + public getBlobField(): GetBlobFieldView { + return new GetBlobFieldView(this); + } + + public getBlobInfo(): GetBlobInfoView { + return new GetBlobInfoView(this); + } + + public listBlobs(): ListBlobsView { + return new ListBlobsView(this); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts new file mode 100644 index 0000000000..19518c6c96 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts @@ -0,0 +1,309 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmclient" + +const ArgBlockIndex = "n"; +const ArgContractHname = "h"; +const ArgFromBlock = "f"; +const ArgRequestID = "u"; +const ArgToBlock = "t"; + +const ResBlockIndex = "n"; +const ResBlockInfo = "i"; +const ResEvent = "e"; +const ResGoverningAddress = "g"; +const ResRequestID = "u"; +const ResRequestIndex = "r"; +const ResRequestProcessed = "p"; +const ResRequestRecord = "d"; +const ResStateControllerAddress = "s"; + +///////////////////////////// controlAddresses ///////////////////////////// + +export class ControlAddressesView extends wasmclient.ClientView { + + public async call(): Promise { + return new ControlAddressesResults(await this.callView("controlAddresses", null)); + } +} + +export class ControlAddressesResults extends wasmclient.ViewResults { + + blockIndex(): wasmclient.Int32 { + return this.res.getInt32(ResBlockIndex); + } + + governingAddress(): wasmclient.Address { + return this.res.getAddress(ResGoverningAddress); + } + + stateControllerAddress(): wasmclient.Address { + return this.res.getAddress(ResStateControllerAddress); + } +} + +///////////////////////////// getBlockInfo ///////////////////////////// + +export class GetBlockInfoView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public blockIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgBlockIndex, v); + } + + public async call(): Promise { + this.args.mandatory(ArgBlockIndex); + return new GetBlockInfoResults(await this.callView("getBlockInfo", this.args)); + } +} + +export class GetBlockInfoResults extends wasmclient.ViewResults { + + blockInfo(): wasmclient.Bytes { + return this.res.getBytes(ResBlockInfo); + } +} + +///////////////////////////// getEventsForBlock ///////////////////////////// + +export class GetEventsForBlockView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public blockIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgBlockIndex, v); + } + + public async call(): Promise { + this.args.mandatory(ArgBlockIndex); + return new GetEventsForBlockResults(await this.callView("getEventsForBlock", this.args)); + } +} + +export class GetEventsForBlockResults extends wasmclient.ViewResults { + + event(): wasmclient.Bytes { + return this.res.getBytes(ResEvent); + } +} + +///////////////////////////// getEventsForContract ///////////////////////////// + +export class GetEventsForContractView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public contractHname(v: wasmclient.Hname): void { + this.args.setHname(ArgContractHname, v); + } + + public fromBlock(v: wasmclient.Int32): void { + this.args.setInt32(ArgFromBlock, v); + } + + public toBlock(v: wasmclient.Int32): void { + this.args.setInt32(ArgToBlock, v); + } + + public async call(): Promise { + this.args.mandatory(ArgContractHname); + return new GetEventsForContractResults(await this.callView("getEventsForContract", this.args)); + } +} + +export class GetEventsForContractResults extends wasmclient.ViewResults { + + event(): wasmclient.Bytes { + return this.res.getBytes(ResEvent); + } +} + +///////////////////////////// getEventsForRequest ///////////////////////////// + +export class GetEventsForRequestView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public requestID(v: wasmclient.RequestID): void { + this.args.setRequestID(ArgRequestID, v); + } + + public async call(): Promise { + this.args.mandatory(ArgRequestID); + return new GetEventsForRequestResults(await this.callView("getEventsForRequest", this.args)); + } +} + +export class GetEventsForRequestResults extends wasmclient.ViewResults { + + event(): wasmclient.Bytes { + return this.res.getBytes(ResEvent); + } +} + +///////////////////////////// getLatestBlockInfo ///////////////////////////// + +export class GetLatestBlockInfoView extends wasmclient.ClientView { + + public async call(): Promise { + return new GetLatestBlockInfoResults(await this.callView("getLatestBlockInfo", null)); + } +} + +export class GetLatestBlockInfoResults extends wasmclient.ViewResults { + + blockIndex(): wasmclient.Int32 { + return this.res.getInt32(ResBlockIndex); + } + + blockInfo(): wasmclient.Bytes { + return this.res.getBytes(ResBlockInfo); + } +} + +///////////////////////////// getRequestIDsForBlock ///////////////////////////// + +export class GetRequestIDsForBlockView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public blockIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgBlockIndex, v); + } + + public async call(): Promise { + this.args.mandatory(ArgBlockIndex); + return new GetRequestIDsForBlockResults(await this.callView("getRequestIDsForBlock", this.args)); + } +} + +export class GetRequestIDsForBlockResults extends wasmclient.ViewResults { + + requestID(): wasmclient.RequestID { + return this.res.getRequestID(ResRequestID); + } +} + +///////////////////////////// getRequestReceipt ///////////////////////////// + +export class GetRequestReceiptView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public requestID(v: wasmclient.RequestID): void { + this.args.setRequestID(ArgRequestID, v); + } + + public async call(): Promise { + this.args.mandatory(ArgRequestID); + return new GetRequestReceiptResults(await this.callView("getRequestReceipt", this.args)); + } +} + +export class GetRequestReceiptResults extends wasmclient.ViewResults { + + blockIndex(): wasmclient.Int32 { + return this.res.getInt32(ResBlockIndex); + } + + requestIndex(): wasmclient.Int16 { + return this.res.getInt16(ResRequestIndex); + } + + requestRecord(): wasmclient.Bytes { + return this.res.getBytes(ResRequestRecord); + } +} + +///////////////////////////// getRequestReceiptsForBlock ///////////////////////////// + +export class GetRequestReceiptsForBlockView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public blockIndex(v: wasmclient.Int32): void { + this.args.setInt32(ArgBlockIndex, v); + } + + public async call(): Promise { + this.args.mandatory(ArgBlockIndex); + return new GetRequestReceiptsForBlockResults(await this.callView("getRequestReceiptsForBlock", this.args)); + } +} + +export class GetRequestReceiptsForBlockResults extends wasmclient.ViewResults { + + requestRecord(): wasmclient.Bytes { + return this.res.getBytes(ResRequestRecord); + } +} + +///////////////////////////// isRequestProcessed ///////////////////////////// + +export class IsRequestProcessedView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public requestID(v: wasmclient.RequestID): void { + this.args.setRequestID(ArgRequestID, v); + } + + public async call(): Promise { + this.args.mandatory(ArgRequestID); + return new IsRequestProcessedResults(await this.callView("isRequestProcessed", this.args)); + } +} + +export class IsRequestProcessedResults extends wasmclient.ViewResults { + + requestProcessed(): string { + return this.res.getString(ResRequestProcessed); + } +} + +///////////////////////////// CoreBlockLogService ///////////////////////////// + +export class CoreBlockLogService extends wasmclient.Service { + + public constructor(cl: wasmclient.ServiceClient) { + super(cl, 0xf538ef2b, null); + } + + public controlAddresses(): ControlAddressesView { + return new ControlAddressesView(this); + } + + public getBlockInfo(): GetBlockInfoView { + return new GetBlockInfoView(this); + } + + public getEventsForBlock(): GetEventsForBlockView { + return new GetEventsForBlockView(this); + } + + public getEventsForContract(): GetEventsForContractView { + return new GetEventsForContractView(this); + } + + public getEventsForRequest(): GetEventsForRequestView { + return new GetEventsForRequestView(this); + } + + public getLatestBlockInfo(): GetLatestBlockInfoView { + return new GetLatestBlockInfoView(this); + } + + public getRequestIDsForBlock(): GetRequestIDsForBlockView { + return new GetRequestIDsForBlockView(this); + } + + public getRequestReceipt(): GetRequestReceiptView { + return new GetRequestReceiptView(this); + } + + public getRequestReceiptsForBlock(): GetRequestReceiptsForBlockView { + return new GetRequestReceiptsForBlockView(this); + } + + public isRequestProcessed(): IsRequestProcessedView { + return new IsRequestProcessedView(this); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts b/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts new file mode 100644 index 0000000000..824907bbc6 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts @@ -0,0 +1,347 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmclient" + +const ArgChainOwner = "oi"; +const ArgFeeColor = "fc"; +const ArgHname = "hn"; +const ArgMaxBlobSize = "bs"; +const ArgMaxEventSize = "es"; +const ArgMaxEventsPerReq = "ne"; +const ArgOwnerFee = "of"; +const ArgStateControllerAddress = "S"; +const ArgValidatorFee = "vf"; + +const ResAllowedStateControllerAddresses = "a"; +const ResChainID = "c"; +const ResChainOwnerID = "o"; +const ResDefaultOwnerFee = "do"; +const ResDefaultValidatorFee = "dv"; +const ResDescription = "d"; +const ResFeeColor = "f"; +const ResMaxBlobSize = "mb"; +const ResMaxEventSize = "me"; +const ResMaxEventsPerReq = "mr"; +const ResOwnerFee = "of"; +const ResValidatorFee = "vf"; + +///////////////////////////// addAllowedStateControllerAddress ///////////////////////////// + +export class AddAllowedStateControllerAddressFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public chainOwner(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgChainOwner, v); + } + + public feeColor(v: wasmclient.Color): void { + this.args.setColor(ArgFeeColor, v); + } + + public stateControllerAddress(v: wasmclient.Address): void { + this.args.setAddress(ArgStateControllerAddress, v); + } + + public async post(): Promise { + this.args.mandatory(ArgChainOwner); + this.args.mandatory(ArgStateControllerAddress); + return await super.post(0x9469d567, this.args); + } +} + +///////////////////////////// claimChainOwnership ///////////////////////////// + +export class ClaimChainOwnershipFunc extends wasmclient.ClientFunc { + + public async post(): Promise { + return await super.post(0x03ff0fc0, null); + } +} + +///////////////////////////// delegateChainOwnership ///////////////////////////// + +export class DelegateChainOwnershipFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public chainOwner(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgChainOwner, v); + } + + public async post(): Promise { + this.args.mandatory(ArgChainOwner); + return await super.post(0x93ecb6ad, this.args); + } +} + +///////////////////////////// removeAllowedStateControllerAddress ///////////////////////////// + +export class RemoveAllowedStateControllerAddressFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public stateControllerAddress(v: wasmclient.Address): void { + this.args.setAddress(ArgStateControllerAddress, v); + } + + public async post(): Promise { + this.args.mandatory(ArgStateControllerAddress); + return await super.post(0x31f69447, this.args); + } +} + +///////////////////////////// rotateStateController ///////////////////////////// + +export class RotateStateControllerFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public stateControllerAddress(v: wasmclient.Address): void { + this.args.setAddress(ArgStateControllerAddress, v); + } + + public async post(): Promise { + this.args.mandatory(ArgStateControllerAddress); + return await super.post(0x244d1038, this.args); + } +} + +///////////////////////////// setChainInfo ///////////////////////////// + +export class SetChainInfoFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public maxBlobSize(v: wasmclient.Int32): void { + this.args.setInt32(ArgMaxBlobSize, v); + } + + public maxEventSize(v: wasmclient.Int16): void { + this.args.setInt16(ArgMaxEventSize, v); + } + + public maxEventsPerReq(v: wasmclient.Int16): void { + this.args.setInt16(ArgMaxEventsPerReq, v); + } + + public ownerFee(v: wasmclient.Int64): void { + this.args.setInt64(ArgOwnerFee, v); + } + + public validatorFee(v: wasmclient.Int64): void { + this.args.setInt64(ArgValidatorFee, v); + } + + public async post(): Promise { + return await super.post(0x702f5d2b, this.args); + } +} + +///////////////////////////// setContractFee ///////////////////////////// + +export class SetContractFeeFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public hname(v: wasmclient.Hname): void { + this.args.setHname(ArgHname, v); + } + + public ownerFee(v: wasmclient.Int64): void { + this.args.setInt64(ArgOwnerFee, v); + } + + public validatorFee(v: wasmclient.Int64): void { + this.args.setInt64(ArgValidatorFee, v); + } + + public async post(): Promise { + this.args.mandatory(ArgHname); + return await super.post(0x8421a42b, this.args); + } +} + +///////////////////////////// setDefaultFee ///////////////////////////// + +export class SetDefaultFeeFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public ownerFee(v: wasmclient.Int64): void { + this.args.setInt64(ArgOwnerFee, v); + } + + public validatorFee(v: wasmclient.Int64): void { + this.args.setInt64(ArgValidatorFee, v); + } + + public async post(): Promise { + return await super.post(0x3310ecd0, this.args); + } +} + +///////////////////////////// getAllowedStateControllerAddresses ///////////////////////////// + +export class GetAllowedStateControllerAddressesView extends wasmclient.ClientView { + + public async call(): Promise { + return new GetAllowedStateControllerAddressesResults(await this.callView("getAllowedStateControllerAddresses", null)); + } +} + +export class GetAllowedStateControllerAddressesResults extends wasmclient.ViewResults { + + allowedStateControllerAddresses(): wasmclient.Bytes { + return this.res.getBytes(ResAllowedStateControllerAddresses); + } +} + +///////////////////////////// getChainInfo ///////////////////////////// + +export class GetChainInfoView extends wasmclient.ClientView { + + public async call(): Promise { + return new GetChainInfoResults(await this.callView("getChainInfo", null)); + } +} + +export class GetChainInfoResults extends wasmclient.ViewResults { + + chainID(): wasmclient.ChainID { + return this.res.getChainID(ResChainID); + } + + chainOwnerID(): wasmclient.AgentID { + return this.res.getAgentID(ResChainOwnerID); + } + + defaultOwnerFee(): wasmclient.Int64 { + return this.res.getInt64(ResDefaultOwnerFee); + } + + defaultValidatorFee(): wasmclient.Int64 { + return this.res.getInt64(ResDefaultValidatorFee); + } + + description(): string { + return this.res.getString(ResDescription); + } + + feeColor(): wasmclient.Color { + return this.res.getColor(ResFeeColor); + } + + maxBlobSize(): wasmclient.Int32 { + return this.res.getInt32(ResMaxBlobSize); + } + + maxEventSize(): wasmclient.Int16 { + return this.res.getInt16(ResMaxEventSize); + } + + maxEventsPerReq(): wasmclient.Int16 { + return this.res.getInt16(ResMaxEventsPerReq); + } +} + +///////////////////////////// getFeeInfo ///////////////////////////// + +export class GetFeeInfoView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public hname(v: wasmclient.Hname): void { + this.args.setHname(ArgHname, v); + } + + public async call(): Promise { + this.args.mandatory(ArgHname); + return new GetFeeInfoResults(await this.callView("getFeeInfo", this.args)); + } +} + +export class GetFeeInfoResults extends wasmclient.ViewResults { + + feeColor(): wasmclient.Color { + return this.res.getColor(ResFeeColor); + } + + ownerFee(): wasmclient.Int64 { + return this.res.getInt64(ResOwnerFee); + } + + validatorFee(): wasmclient.Int64 { + return this.res.getInt64(ResValidatorFee); + } +} + +///////////////////////////// getMaxBlobSize ///////////////////////////// + +export class GetMaxBlobSizeView extends wasmclient.ClientView { + + public async call(): Promise { + return new GetMaxBlobSizeResults(await this.callView("getMaxBlobSize", null)); + } +} + +export class GetMaxBlobSizeResults extends wasmclient.ViewResults { + + maxBlobSize(): wasmclient.Int32 { + return this.res.getInt32(ResMaxBlobSize); + } +} + +///////////////////////////// CoreGovernanceService ///////////////////////////// + +export class CoreGovernanceService extends wasmclient.Service { + + public constructor(cl: wasmclient.ServiceClient) { + super(cl, 0x17cf909f, null); + } + + public addAllowedStateControllerAddress(): AddAllowedStateControllerAddressFunc { + return new AddAllowedStateControllerAddressFunc(this); + } + + public claimChainOwnership(): ClaimChainOwnershipFunc { + return new ClaimChainOwnershipFunc(this); + } + + public delegateChainOwnership(): DelegateChainOwnershipFunc { + return new DelegateChainOwnershipFunc(this); + } + + public removeAllowedStateControllerAddress(): RemoveAllowedStateControllerAddressFunc { + return new RemoveAllowedStateControllerAddressFunc(this); + } + + public rotateStateController(): RotateStateControllerFunc { + return new RotateStateControllerFunc(this); + } + + public setChainInfo(): SetChainInfoFunc { + return new SetChainInfoFunc(this); + } + + public setContractFee(): SetContractFeeFunc { + return new SetContractFeeFunc(this); + } + + public setDefaultFee(): SetDefaultFeeFunc { + return new SetDefaultFeeFunc(this); + } + + public getAllowedStateControllerAddresses(): GetAllowedStateControllerAddressesView { + return new GetAllowedStateControllerAddressesView(this); + } + + public getChainInfo(): GetChainInfoView { + return new GetChainInfoView(this); + } + + public getFeeInfo(): GetFeeInfoView { + return new GetFeeInfoView(this); + } + + public getMaxBlobSize(): GetMaxBlobSizeView { + return new GetMaxBlobSizeView(this); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts new file mode 100644 index 0000000000..2f8164f7f1 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts @@ -0,0 +1,143 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +// (Re-)generated by schema tool +// >>>> DO NOT CHANGE THIS FILE! <<<< +// Change the json schema instead + +import * as wasmclient from "wasmclient" + +const ArgDeployer = "dp"; +const ArgDescription = "ds"; +const ArgHname = "hn"; +const ArgName = "nm"; +const ArgProgramHash = "ph"; + +const ResContractFound = "cf"; +const ResContractRecData = "dt"; +const ResContractRegistry = "r"; + +///////////////////////////// deployContract ///////////////////////////// + +export class DeployContractFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public description(v: string): void { + this.args.setString(ArgDescription, v); + } + + public name(v: string): void { + this.args.setString(ArgName, v); + } + + public programHash(v: wasmclient.Hash): void { + this.args.setHash(ArgProgramHash, v); + } + + public async post(): Promise { + this.args.mandatory(ArgName); + this.args.mandatory(ArgProgramHash); + return await super.post(0x28232c27, this.args); + } +} + +///////////////////////////// grantDeployPermission ///////////////////////////// + +export class GrantDeployPermissionFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public deployer(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgDeployer, v); + } + + public async post(): Promise { + this.args.mandatory(ArgDeployer); + return await super.post(0xf440263a, this.args); + } +} + +///////////////////////////// revokeDeployPermission ///////////////////////////// + +export class RevokeDeployPermissionFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public deployer(v: wasmclient.AgentID): void { + this.args.setAgentID(ArgDeployer, v); + } + + public async post(): Promise { + this.args.mandatory(ArgDeployer); + return await super.post(0x850744f1, this.args); + } +} + +///////////////////////////// findContract ///////////////////////////// + +export class FindContractView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public hname(v: wasmclient.Hname): void { + this.args.setHname(ArgHname, v); + } + + public async call(): Promise { + this.args.mandatory(ArgHname); + return new FindContractResults(await this.callView("findContract", this.args)); + } +} + +export class FindContractResults extends wasmclient.ViewResults { + + contractFound(): wasmclient.Bytes { + return this.res.getBytes(ResContractFound); + } + + contractRecData(): wasmclient.Bytes { + return this.res.getBytes(ResContractRecData); + } +} + +///////////////////////////// getContractRecords ///////////////////////////// + +export class GetContractRecordsView extends wasmclient.ClientView { + + public async call(): Promise { + return new GetContractRecordsResults(await this.callView("getContractRecords", null)); + } +} + +export class GetContractRecordsResults extends wasmclient.ViewResults { + + contractRegistry(): wasmclient.Bytes { + return this.res.getBytes(ResContractRegistry); + } +} + +///////////////////////////// CoreRootService ///////////////////////////// + +export class CoreRootService extends wasmclient.Service { + + public constructor(cl: wasmclient.ServiceClient) { + super(cl, 0xcebf5908, null); + } + + public deployContract(): DeployContractFunc { + return new DeployContractFunc(this); + } + + public grantDeployPermission(): GrantDeployPermissionFunc { + return new GrantDeployPermissionFunc(this); + } + + public revokeDeployPermission(): RevokeDeployPermissionFunc { + return new RevokeDeployPermissionFunc(this); + } + + public findContract(): FindContractView { + return new FindContractView(this); + } + + public getContractRecords(): GetContractRecordsView { + return new GetContractRecordsView(this); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index b2ce869370..3344e96557 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -5,6 +5,8 @@ import * as wasmclient from "./index"; import { IKeyPair } from "./crypto"; import { IOnLedger } from "./goshimmer/models/on_ledger"; import { Colors } from "./colors"; +import { Buffer } from './buffer'; +import { Hash } from './crypto'; export type EventHandlers = Map void>; @@ -57,6 +59,11 @@ export class Service { } } + // overrides default contract name + public serviceContractName(contractName: string): void { + this.scHname = Hash.from(Buffer.from(contractName)).readUInt32LE(0) + } + public async waitRequest(reqID: wasmclient.RequestID): Promise { await this.serviceClient.waspClient.waitRequest(this.serviceClient.configuration.chainId, reqID); } diff --git a/tools/schema/generator/clientbase.go b/tools/schema/generator/clientbase.go index 31f7a82fde..4ccbf673c2 100644 --- a/tools/schema/generator/clientbase.go +++ b/tools/schema/generator/clientbase.go @@ -13,11 +13,14 @@ type ClientBase struct { func (g *ClientBase) Generate() error { g.folder = g.rootFolder + "/" + g.s.PackageName + "client/" + if g.s.CoreContracts { + g.folder = g.rootFolder + "/wasmclient/" + g.s.PackageName + "/" + } err := os.MkdirAll(g.folder, 0o755) if err != nil { return err } - info, err := os.Stat(g.folder + "events" + g.extension) + info, err := os.Stat(g.folder + "service" + g.extension) if err == nil && info.ModTime().After(g.s.SchemaTime) { fmt.Printf("skipping %s code generation\n", g.language) return nil @@ -28,7 +31,7 @@ func (g *ClientBase) Generate() error { } func (g *ClientBase) generateCode() error { - err := g.createSourceFile("events", true) + err := g.createSourceFile("events", !g.s.CoreContracts) if err != nil { return err } @@ -36,7 +39,10 @@ func (g *ClientBase) generateCode() error { if err != nil { return err } - return g.generateFuncs(g.appendEvents) + if !g.s.CoreContracts { + return g.generateFuncs(g.appendEvents) + } + return nil } func (g *ClientBase) appendEvents(existing model.StringMap) { diff --git a/tools/schema/generator/generator_tsclient.go b/tools/schema/generator/generator_tsclient.go index e7ec9b26c8..97e1e98baa 100644 --- a/tools/schema/generator/generator_tsclient.go +++ b/tools/schema/generator/generator_tsclient.go @@ -23,6 +23,9 @@ func (g *TsClientGenerator) Generate() error { if err != nil { return err } + if g.s.CoreContracts { + return nil + } // now generate language-specific files diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index a35270a4c9..41e0710b2c 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -20,10 +20,16 @@ type $PkgName$+Service struct { func New$PkgName$+Service(cl *wasmclient.ServiceClient, chainID string) (*$PkgName$+Service, error) { s := &$PkgName$+Service{} - err := s.Service.Init(cl, chainID, 0x$hscName, EventHandlers) +$#set eventHandlers EventHandlers +$#if core noEventHandlers + err := s.Service.Init(cl, chainID, 0x$hscName, $eventHandlers) return s, err } $#each func serviceFunction +`, + // ******************************* + "noEventHandlers": ` +$#set eventHandlers nil `, // ******************************* "constArg": ` @@ -86,7 +92,7 @@ func (f *$FuncName$Kind) Call() $FuncName$+Results { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs f.ClientView.Call("$funcName", $args) - return $FuncName$+Results { res: f.Results() } + return $FuncName$+Results{res: f.Results()} } $#if result resultStruct `, @@ -139,7 +145,7 @@ func (r *$FuncName$+Results) $FldName$+Exists() bool { "serviceFunction": ` func (s *$PkgName$+Service) $FuncName() $FuncName$Kind { - return $FuncName$Kind{ Client$Kind: s.AsClient$Kind() } + return $FuncName$Kind{Client$Kind: s.AsClient$Kind()} } `, } diff --git a/tools/schema/generator/tsclienttemplates/alltemplates.go b/tools/schema/generator/tsclienttemplates/alltemplates.go index 08870a81cd..fb9cfbe3b4 100644 --- a/tools/schema/generator/tsclienttemplates/alltemplates.go +++ b/tools/schema/generator/tsclienttemplates/alltemplates.go @@ -93,5 +93,17 @@ var common = map[string]string{ "node_modules" ], } +`, + // ******************************* + "importEvents": ` +import * as events from "./events" +`, + // ******************************* + "importService": ` +import * as service from "./service" +`, + // ******************************* + "importWasmLib": ` +import * as wasmclient from "wasmclient" `, } diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go index afaa4f1207..7a78b1ec15 100644 --- a/tools/schema/generator/tsclienttemplates/events.go +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -3,7 +3,7 @@ package tsclienttemplates var eventsTs = map[string]string{ // ******************************* "events.ts": ` -import * as wasmclient from "wasmclient" +$#emit importWasmLib import * as app from "./$package" export const eventHandlers: wasmclient.EventHandlers = { diff --git a/tools/schema/generator/tsclienttemplates/funcs.go b/tools/schema/generator/tsclienttemplates/funcs.go index 37f15b7fab..02e361eaf3 100644 --- a/tools/schema/generator/tsclienttemplates/funcs.go +++ b/tools/schema/generator/tsclienttemplates/funcs.go @@ -3,8 +3,8 @@ package tsclienttemplates var funcsTs = map[string]string{ // ******************************* "funcs.ts": ` -import * as events from "./events" -import * as service from "./service" +$#emit importEvents +$#emit importService $#each events funcSignature `, // ******************************* diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 3a74d2b141..7b50af2365 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -3,8 +3,8 @@ package tsclienttemplates var serviceTs = map[string]string{ // ******************************* "service.ts": ` -import * as wasmclient from "wasmclient" -import * as events from "./events" +$#emit importWasmLib +$#if core else importEvents $#each params constArg @@ -16,10 +16,16 @@ $#each func funcStruct export class $PkgName$+Service extends wasmclient.Service { public constructor(cl: wasmclient.ServiceClient) { - super(cl, 0x$hscName, events.eventHandlers); +$#set eventHandlers events.eventHandlers +$#if core noEventHandlers + super(cl, 0x$hscName, $eventHandlers); } $#each func serviceFunction } +`, + // ******************************* + "noEventHandlers": ` +$#set eventHandlers null `, // ******************************* "constArg": ` From 314dbb72acdec914b91b59df04714f22f160e558 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 6 Jan 2022 18:25:38 -0800 Subject: [PATCH 063/120] Fixed disappeared code --- packages/vm/wasmlib/ts/wasmclient/service.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 3344e96557..ba675f1471 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -2,11 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import * as wasmclient from "./index"; -import { IKeyPair } from "./crypto"; -import { IOnLedger } from "./goshimmer/models/on_ledger"; -import { Colors } from "./colors"; -import { Buffer } from './buffer'; -import { Hash } from './crypto'; +import {Hash, IKeyPair} from "./crypto"; +import {IOnLedger} from "./goshimmer/models/on_ledger"; +import {Colors} from "./colors"; +import {Buffer} from './buffer'; export type EventHandlers = Map void>; @@ -18,10 +17,12 @@ export class Service { public scHname: wasmclient.Hname; private waspWebSocketUrl: string = ""; - constructor(client: wasmclient.ServiceClient, scHname: wasmclient.Hname, eventHandlers: EventHandlers) { + constructor(client: wasmclient.ServiceClient, scHname: wasmclient.Hname, eventHandlers: EventHandlers | null) { this.serviceClient = client; this.scHname = scHname; - this.configureWebSocketsEventHandlers(eventHandlers); + if (eventHandlers != null) { + this.configureWebSocketsEventHandlers(eventHandlers); + } } public async callView(viewName: string, args: wasmclient.Arguments): Promise { From 0dac06d43582e6b455e205a26b1c73cf6211d4c8 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 6 Jan 2022 18:37:52 -0800 Subject: [PATCH 064/120] Fixed event map generation --- contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts | 6 +++--- tools/schema/generator/tsclienttemplates/events.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts index 2e11655cef..6d066c0cf0 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/events.ts @@ -8,9 +8,9 @@ import * as wasmclient from "wasmclient" import * as app from "./testwasmlib" -export const eventHandlers: wasmclient.EventHandlers = { - "testwasmlib.test": (msg: string[]) => app.onTestWasmLibTest(new EventTest(msg)), -}; +export const eventHandlers: wasmclient.EventHandlers = new Map([ + ["testwasmlib.test", (msg: string[]) => app.onTestWasmLibTest(new EventTest(msg))], +]); export class EventTest extends wasmclient.Event { public readonly address: wasmclient.Address; diff --git a/tools/schema/generator/tsclienttemplates/events.go b/tools/schema/generator/tsclienttemplates/events.go index 7a78b1ec15..b7bef5f608 100644 --- a/tools/schema/generator/tsclienttemplates/events.go +++ b/tools/schema/generator/tsclienttemplates/events.go @@ -6,14 +6,14 @@ var eventsTs = map[string]string{ $#emit importWasmLib import * as app from "./$package" -export const eventHandlers: wasmclient.EventHandlers = { +export const eventHandlers: wasmclient.EventHandlers = new Map([ $#each events eventHandler -}; +]); $#each events eventClass `, // ******************************* "eventHandler": ` - "$package.$evtName": (msg: string[]) => app.on$PkgName$EvtName(new Event$EvtName(msg)), + ["$package.$evtName", (msg: string[]) => app.on$PkgName$EvtName(new Event$EvtName(msg))], `, // ******************************* "eventClass": ` From 79f0aaf523a64627b369f930c4b1d99fc0a9639c Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Fri, 7 Jan 2022 03:51:41 +0100 Subject: [PATCH 065/120] Changed logic to use generated CoreAccountsService --- .../ts/wasmclient/goshimmer/goshimmerclient.ts | 16 ++++++++-------- .../vm/wasmlib/ts/wasmclient/serviceclient.ts | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts index 8b9e15cbaf..26ab296fee 100644 --- a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts @@ -16,8 +16,8 @@ import { IOnLedger, OnLedgerHelper } from "./models/on_ledger"; import { ISendTransactionRequest, ISendTransactionResponse, ITransaction, Transaction } from "./models/transaction"; import { Wallet } from "./wallet/wallet"; import { Colors } from "../colors"; -import { Configuration, Transfer } from ".."; -import { AccountsService } from "../core_services/accounts_service"; +import { AgentID, Configuration, Transfer } from ".."; +import { CoreAccountsService } from "../coreaccounts/service"; interface GoShimmerClientConfiguration { APIUrl: string; @@ -25,12 +25,12 @@ interface GoShimmerClientConfiguration { } export class GoShimmerClient { - private accountsService: AccountsService; + private coreAccountsService: CoreAccountsService; private readonly goShimmerConfiguration: GoShimmerClientConfiguration; private readonly powManager: PoWWorkerManager = new PoWWorkerManager(); - constructor(configuration : Configuration, accountsService: AccountsService) { - this.accountsService = accountsService; + constructor(configuration : Configuration, coreAccountsService: CoreAccountsService) { + this.coreAccountsService = coreAccountsService; this.goShimmerConfiguration = { APIUrl: configuration.goShimmerApiUrl, SeedUnsafe: configuration.seed }; } @@ -171,9 +171,9 @@ export class GoShimmerClient { ); } - public async depositToAccountInChain(keypair: IKeyPair, destinationAddress: string, amount: bigint) { - const depositfunc = this.accountsService.deposit(); - depositfunc.address(destinationAddress); + public async depositIOTAToAccountInChain(keypair: IKeyPair, destinationAgentID: AgentID, amount: bigint) { + const depositfunc = this.coreAccountsService.deposit(); + depositfunc.agentID(destinationAgentID); depositfunc.transfer(Transfer.iotas(amount)); depositfunc.sign(keypair); depositfunc.post(); diff --git a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts index 752d0c8c55..66e93df086 100644 --- a/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/serviceclient.ts @@ -3,7 +3,7 @@ import * as wasmclient from "./index"; import { Configuration, IConfiguration } from "./configuration"; -import { AccountsService } from "./core_services/accounts_service"; +import { CoreAccountsService } from "./coreaccounts/service"; export class ServiceClient { waspClient: wasmclient.WaspClient; @@ -13,7 +13,7 @@ export class ServiceClient { constructor(configuration: Configuration) { this.configuration = configuration; this.waspClient = new wasmclient.WaspClient(configuration.waspApiUrl); - const accountsService = new AccountsService(this); + const accountsService = new CoreAccountsService(this); this.goShimmerClient = new wasmclient.GoShimmerClient(configuration, accountsService); } From 9a4cebad770992c856a3f10a17faae0f31132810 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Fri, 7 Jan 2022 03:52:00 +0100 Subject: [PATCH 066/120] Changed colors to use constants --- packages/vm/wasmlib/ts/wasmclient/transfer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/transfer.ts b/packages/vm/wasmlib/ts/wasmclient/transfer.ts index 660e80a979..267eb32e12 100644 --- a/packages/vm/wasmlib/ts/wasmclient/transfer.ts +++ b/packages/vm/wasmlib/ts/wasmclient/transfer.ts @@ -10,7 +10,7 @@ export class Transfer { private xfer = new Map(); static iotas(amount: wasmclient.Uint64): Transfer { - return Transfer.tokens("IOTA", amount); + return Transfer.tokens(Colors.IOTA_COLOR, amount); } static tokens(color: string, amount: wasmclient.Uint64): Transfer { @@ -20,8 +20,8 @@ export class Transfer { } set(color: string, amount: wasmclient.Uint64) { - if (color == "IOTA") { - color = "11111111111111111111111111111111" + if (color == Colors.IOTA_COLOR) { + color = Colors.IOTA_COLOR_STRING } this.xfer.set(color, amount); } From 2d22a4aad254cbf1accc4a1b36f6b291bf9caa8f Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Fri, 7 Jan 2022 03:52:32 +0100 Subject: [PATCH 067/120] Removed redundant structure. --- .../wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts index d834254738..1dc53d9243 100644 --- a/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/models/unspent_outputs.ts @@ -1,4 +1,5 @@ import type { IResponse } from '../../api_common/response_models'; +import { ColorCollection } from '../../colors'; export interface IUnspentOutputsRequest { addresses: string[]; @@ -33,8 +34,6 @@ export interface IUnspentOutputAddress { base58: string; } -export type ColorCollection = { [key: string]: bigint }; - export interface IUnspentOutputMap { address: string; outputs: { From 074d6678f6b763c73bd3c398385e64dcf3934386 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Fri, 7 Jan 2022 03:53:54 +0100 Subject: [PATCH 068/120] Replaced null event handlers for new maps --- packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts | 2 +- packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts | 2 +- packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts | 2 +- packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts | 2 +- packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts index 0c7258d4de..65c281eaa4 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts @@ -137,7 +137,7 @@ export class TotalAssetsResults extends wasmclient.ViewResults { export class CoreAccountsService extends wasmclient.Service { public constructor(cl: wasmclient.ServiceClient) { - super(cl, 0x3c4b5e02, null); + super(cl, 0x3c4b5e02, new Map()); } public deposit(): DepositFunc { diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts index 52ae9ceb13..5edd7751ef 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts @@ -106,7 +106,7 @@ export class ListBlobsResults extends wasmclient.ViewResults { export class CoreBlobService extends wasmclient.Service { public constructor(cl: wasmclient.ServiceClient) { - super(cl, 0xfd91bc63, null); + super(cl, 0xfd91bc63, new Map()); } public storeBlob(): StoreBlobFunc { diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts index 19518c6c96..0705ca882c 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts @@ -264,7 +264,7 @@ export class IsRequestProcessedResults extends wasmclient.ViewResults { export class CoreBlockLogService extends wasmclient.Service { public constructor(cl: wasmclient.ServiceClient) { - super(cl, 0xf538ef2b, null); + super(cl, 0xf538ef2b, new Map()); } public controlAddresses(): ControlAddressesView { diff --git a/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts b/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts index 824907bbc6..c37ec7c033 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts @@ -294,7 +294,7 @@ export class GetMaxBlobSizeResults extends wasmclient.ViewResults { export class CoreGovernanceService extends wasmclient.Service { public constructor(cl: wasmclient.ServiceClient) { - super(cl, 0x17cf909f, null); + super(cl, 0x17cf909f, new Map()); } public addAllowedStateControllerAddress(): AddAllowedStateControllerAddressFunc { diff --git a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts index 2f8164f7f1..470ba275f5 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts @@ -118,7 +118,7 @@ export class GetContractRecordsResults extends wasmclient.ViewResults { export class CoreRootService extends wasmclient.Service { public constructor(cl: wasmclient.ServiceClient) { - super(cl, 0xcebf5908, null); + super(cl, 0xcebf5908, new Map()); } public deployContract(): DeployContractFunc { From d2ce1e0644f5e06e684e20d147b4bd596934f77c Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Thu, 6 Jan 2022 19:04:07 -0800 Subject: [PATCH 069/120] Use new Map() instead of null --- packages/vm/wasmlib/ts/wasmclient/service.ts | 4 ++-- tools/schema/generator/tsclienttemplates/service.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index ba675f1471..dd76312750 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -17,10 +17,10 @@ export class Service { public scHname: wasmclient.Hname; private waspWebSocketUrl: string = ""; - constructor(client: wasmclient.ServiceClient, scHname: wasmclient.Hname, eventHandlers: EventHandlers | null) { + constructor(client: wasmclient.ServiceClient, scHname: wasmclient.Hname, eventHandlers: EventHandlers) { this.serviceClient = client; this.scHname = scHname; - if (eventHandlers != null) { + if (eventHandlers.size != 0) { this.configureWebSocketsEventHandlers(eventHandlers); } } diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 7b50af2365..86462a2823 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -25,7 +25,7 @@ $#each func serviceFunction `, // ******************************* "noEventHandlers": ` -$#set eventHandlers null +$#set eventHandlers new Map() `, // ******************************* "constArg": ` From 826a8fba79cd2b699875287a2f14827250ad183d Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Fri, 7 Jan 2022 06:10:24 +0100 Subject: [PATCH 070/120] Fixed bug which caused failure when depositing in chain --- .../wasmlib/ts/wasmclient/crypto/ed25519.ts | 23 +++++++++++++++++++ .../vm/wasmlib/ts/wasmclient/crypto/seed.ts | 15 ++++-------- .../wasmclient/goshimmer/goshimmerclient.ts | 15 ++++++------ packages/vm/wasmlib/ts/wasmclient/service.ts | 3 +-- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts index 2991e2311f..7dced5196c 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts @@ -1,11 +1,34 @@ import nacl from 'tweetnacl'; import { Buffer } from '../buffer'; +import { Base58 } from "./base58"; +import { Hash } from "./hash"; +import { Seed } from "./seed"; export interface IKeyPair { publicKey: Buffer; secretKey: Buffer; } +/** + * Calculates the address for public key + * @param keyPair The key pair used to calculate the address + * @returns The generated address. + */ + export function getAddress(keyPair: IKeyPair): string { + const publicKeyBuffer = Buffer.from(keyPair.publicKey); + return getAddressFromPublicKeyBuffer(publicKeyBuffer); +} + +export function getAddressFromPublicKeyBuffer(publicKeyBuffer: Buffer): string { + const digest = Hash.from(publicKeyBuffer); + + const buffer = Buffer.alloc(Seed.SEED_SIZE + 1); + buffer[0] = ED25519.VERSION; + Buffer.from(digest).copy(buffer, 1); + + return Base58.encode(buffer); +} + /** * Class to help with ED25519 Signature scheme. */ diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts index 8ea5b3753e..3654f66792 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/seed.ts @@ -1,6 +1,6 @@ import { Base58 } from './base58'; import { Buffer } from '../buffer'; -import { ED25519, IKeyPair } from './ed25519'; +import { ED25519, getAddressFromPublicKeyBuffer, IKeyPair } from './ed25519'; import { Hash } from './hash'; export class Seed { @@ -70,16 +70,9 @@ export class Seed { * @param index The index of the address to generate. * @returns The generated address. */ - public static generateAddress(seed: Buffer, index: number): string { - const {publicKey} = Seed.generateKeyPair(seed, index); - - const digest = Hash.from(publicKey); - - const buffer = Buffer.alloc(Seed.SEED_SIZE + 1); - buffer[0] = ED25519.VERSION; - Buffer.from(digest).copy(buffer, 1); - - return Base58.encode(buffer); + public static generateAddress(seed: Buffer, index: number): string { + const { publicKey: publicKeyBuffer } = Seed.generateKeyPair(seed, index); + return getAddressFromPublicKeyBuffer(publicKeyBuffer); } /** diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts index 26ab296fee..0b534d01d3 100644 --- a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts @@ -10,13 +10,13 @@ import { PoWWorkerManager } from "./pow_web_worker/pow_worker_manager"; import * as requestSender from "../api_common/request_sender"; -import { Base58, IKeyPair } from "../crypto"; +import { Base58, getAddress, IKeyPair } from "../crypto"; import { IOnLedger, OnLedgerHelper } from "./models/on_ledger"; import { ISendTransactionRequest, ISendTransactionResponse, ITransaction, Transaction } from "./models/transaction"; import { Wallet } from "./wallet/wallet"; import { Colors } from "../colors"; -import { AgentID, Configuration, Transfer } from ".."; +import { Configuration, Transfer } from ".."; import { CoreAccountsService } from "../coreaccounts/service"; interface GoShimmerClientConfiguration { @@ -120,8 +120,7 @@ export class GoShimmerClient { return response; } - public async sendOnLedgerRequest( - address: string, + public async postOnLedgerRequest( chainId: string, payload: IOnLedger, transfer: bigint = 1n, @@ -133,6 +132,7 @@ export class GoShimmerClient { const wallet = new Wallet(this); + const address = getAddress(keyPair); const unspents = await wallet.getUnspentOutputs(address); const consumedOutputs = wallet.determineOutputsToConsume(unspents, transfer); const { inputs, consumedFunds } = wallet.buildInputs(consumedOutputs); @@ -171,11 +171,12 @@ export class GoShimmerClient { ); } - public async depositIOTAToAccountInChain(keypair: IKeyPair, destinationAgentID: AgentID, amount: bigint) { + public async depositIOTAToAccountInChain(keypair: IKeyPair, amount: bigint) { const depositfunc = this.coreAccountsService.deposit(); - depositfunc.agentID(destinationAgentID); + // TODO: add support for depositing into accounts other than the caller's + //depositfunc.agentID(destinationAgentID); depositfunc.transfer(Transfer.iotas(amount)); depositfunc.sign(keypair); - depositfunc.post(); + await depositfunc.post(); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index ba675f1471..1a9affd208 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -46,7 +46,6 @@ export class Service { const requestID = await this.serviceClient.waspClient.postOffLedgerRequest(chainId, this.scHname, hFuncName, args, transfer, keyPair); return requestID; } else { - const address = args.getString("address"); const payload: IOnLedger = { contract: this.scHname, entrypoint: hFuncName, @@ -54,7 +53,7 @@ export class Service { //arguments : args }; const transferAmount = transfer.get(Colors.IOTA_COLOR); - const transactionID = await this.serviceClient.goShimmerClient.sendOnLedgerRequest(address, chainId, payload, transferAmount, keyPair); + const transactionID = await this.serviceClient.goShimmerClient.postOnLedgerRequest(chainId, payload, transferAmount, keyPair); if (!transactionID) throw new Error("No transaction id"); return transactionID; } From a9734b242b5d7ce0d52dc388e18cb57f1d65425d Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Fri, 7 Jan 2022 06:11:55 +0100 Subject: [PATCH 071/120] Minor change. Changed function summary. --- packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts index 7dced5196c..bcb5e48ddd 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts @@ -10,7 +10,7 @@ export interface IKeyPair { } /** - * Calculates the address for public key + * Calculates the address for key pair * @param keyPair The key pair used to calculate the address * @returns The generated address. */ From 4d875cd4404368ae19ffeca63f40b0abc527852c Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Fri, 7 Jan 2022 10:00:05 -0800 Subject: [PATCH 072/120] Get rid of unnecessary import aliases --- contracts/wasm/testcore/test/testcore_test.go | 22 +++++++++---------- .../testcore/sbtests/sbtestsc/impl_misc.go | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/contracts/wasm/testcore/test/testcore_test.go b/contracts/wasm/testcore/test/testcore_test.go index 19cbe1e0fe..9ca6847fbd 100644 --- a/contracts/wasm/testcore/test/testcore_test.go +++ b/contracts/wasm/testcore/test/testcore_test.go @@ -9,9 +9,9 @@ import ( "github.com/iotaledger/wasp/packages/util" "github.com/iotaledger/wasp/packages/vm/core/testcore/sbtests/sbtestsc" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib" - coreaccounts2 "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coreaccounts" - coregovernance2 "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coregovernance" - coreroot2 "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coreroot" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coreaccounts" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coregovernance" + "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmlib/coreroot" "github.com/iotaledger/wasp/packages/vm/wasmsolo" "github.com/stretchr/testify/require" ) @@ -131,8 +131,8 @@ func originatorBalanceReducedBy(ctx *wasmsolo.SoloContext, w bool, minus uint64) } func deposit(t *testing.T, ctx *wasmsolo.SoloContext, user, target *wasmsolo.SoloAgent, amount int64) { - ctxAcc := ctx.SoloContextForCore(t, coreaccounts2.ScName, coreaccounts2.OnLoad) - f := coreaccounts2.ScFuncs.Deposit(ctxAcc.Sign(user)) + ctxAcc := ctx.SoloContextForCore(t, coreaccounts.ScName, coreaccounts.OnLoad) + f := coreaccounts.ScFuncs.Deposit(ctxAcc.Sign(user)) if target != nil { f.Params.AgentID().SetValue(target.ScAgentID()) } @@ -141,16 +141,16 @@ func deposit(t *testing.T, ctx *wasmsolo.SoloContext, user, target *wasmsolo.Sol } func setDeployer(t *testing.T, ctx *wasmsolo.SoloContext, deployer *wasmsolo.SoloAgent) { - ctxRoot := ctx.SoloContextForCore(t, coreroot2.ScName, coreroot2.OnLoad) - f := coreroot2.ScFuncs.GrantDeployPermission(ctxRoot) + ctxRoot := ctx.SoloContextForCore(t, coreroot.ScName, coreroot.OnLoad) + f := coreroot.ScFuncs.GrantDeployPermission(ctxRoot) f.Params.Deployer().SetValue(deployer.ScAgentID()) f.Func.TransferIotas(1).Post() require.NoError(t, ctxRoot.Err) } func setOwnerFee(t *testing.T, ctx *wasmsolo.SoloContext, amount int64) { - ctxGov := ctx.SoloContextForCore(t, coregovernance2.ScName, coregovernance2.OnLoad) - f := coregovernance2.ScFuncs.SetContractFee(ctxGov) + ctxGov := ctx.SoloContextForCore(t, coregovernance.ScName, coregovernance.OnLoad) + f := coregovernance.ScFuncs.SetContractFee(ctxGov) f.Params.Hname().SetValue(testcore.HScName) f.Params.OwnerFee().SetValue(amount) f.Func.TransferIotas(1).Post() @@ -158,8 +158,8 @@ func setOwnerFee(t *testing.T, ctx *wasmsolo.SoloContext, amount int64) { } func withdraw(t *testing.T, ctx *wasmsolo.SoloContext, user *wasmsolo.SoloAgent) { - ctxAcc := ctx.SoloContextForCore(t, coreaccounts2.ScName, coreaccounts2.OnLoad) - f := coreaccounts2.ScFuncs.Withdraw(ctxAcc.Sign(user)) + ctxAcc := ctx.SoloContextForCore(t, coreaccounts.ScName, coreaccounts.OnLoad) + f := coreaccounts.ScFuncs.Withdraw(ctxAcc.Sign(user)) f.Func.TransferIotas(1).Post() require.NoError(t, ctxAcc.Err) } diff --git a/packages/vm/core/testcore/sbtests/sbtestsc/impl_misc.go b/packages/vm/core/testcore/sbtests/sbtestsc/impl_misc.go index d2e8e9c108..2d5bab077b 100644 --- a/packages/vm/core/testcore/sbtests/sbtestsc/impl_misc.go +++ b/packages/vm/core/testcore/sbtests/sbtestsc/impl_misc.go @@ -2,7 +2,7 @@ package sbtestsc import ( "github.com/iotaledger/wasp/packages/iscp" - assert2 "github.com/iotaledger/wasp/packages/iscp/assert" + "github.com/iotaledger/wasp/packages/iscp/assert" "github.com/iotaledger/wasp/packages/kv" "github.com/iotaledger/wasp/packages/kv/codec" "github.com/iotaledger/wasp/packages/kv/dict" @@ -60,7 +60,7 @@ func runRecursion(ctx iscp.Sandbox) (dict.Dict, error) { func getFibonacci(ctx iscp.SandboxView) (dict.Dict, error) { params := kvdecoder.New(ctx.Params(), ctx.Log()) - a := assert2.NewAssert(ctx.Log()) + a := assert.NewAssert(ctx.Log()) callInt := params.MustGetInt64(ParamIntParamValue) ctx.Log().Infof("fibonacci( %d )", callInt) From e21e8b4bd65a69c2bd03c1dde49677edf9fb8216 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Fri, 7 Jan 2022 15:16:49 -0800 Subject: [PATCH 073/120] Added onLedgerRequest() to ClientFunc --- .../vm/wasmlib/go/wasmclient/clientfunc.go | 14 +++++++--- packages/vm/wasmlib/go/wasmclient/service.go | 12 +++++++- .../vm/wasmlib/ts/wasmclient/clientfunc.ts | 11 ++++++-- packages/vm/wasmlib/ts/wasmclient/service.ts | 28 ++++++++++--------- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/packages/vm/wasmlib/go/wasmclient/clientfunc.go b/packages/vm/wasmlib/go/wasmclient/clientfunc.go index 71eb63d7f3..6adc8e144c 100644 --- a/packages/vm/wasmlib/go/wasmclient/clientfunc.go +++ b/packages/vm/wasmlib/go/wasmclient/clientfunc.go @@ -6,9 +6,15 @@ package wasmclient import "github.com/iotaledger/hive.go/crypto/ed25519" type ClientFunc struct { - svc *Service - keyPair *ed25519.KeyPair - xfer *Transfer + svc *Service + keyPair *ed25519.KeyPair + onLedger bool + xfer *Transfer +} + +// OnLedgerRequest can override the default off-ledger to on-ledger posting +func (f *ClientFunc) OnLedgerRequest(onLedger bool) { + f.onLedger = onLedger } // Post sends a request to the smart contract service @@ -19,7 +25,7 @@ func (f *ClientFunc) Post(hFuncName uint32, args *Arguments) Request { if keyPair == nil { keyPair = f.svc.keyPair } - return f.svc.PostRequest(hFuncName, args, f.xfer, keyPair) + return f.svc.PostRequest(hFuncName, args, f.xfer, keyPair, f.onLedger) } // Sign optionally overrides the default keypair from the service diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go index d9eeb69817..e68da2abfe 100644 --- a/packages/vm/wasmlib/go/wasmclient/service.go +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -55,7 +55,7 @@ func (s *Service) CallView(viewName string, args *Arguments) (dict.Dict, error) return s.waspClient.CallView(s.chainID, s.scHname, viewName, args.args) } -func (s *Service) PostRequest(hFuncName uint32, args *Arguments, transfer *Transfer, keyPair *ed25519.KeyPair) Request { +func (s *Service) PostRequest(hFuncName uint32, args *Arguments, transfer *Transfer, keyPair *ed25519.KeyPair, onLedger bool) Request { bal, err := makeBalances(transfer) if err != nil { return Request{err: err} @@ -64,6 +64,11 @@ func (s *Service) PostRequest(hFuncName uint32, args *Arguments, transfer *Trans if args != nil { reqArgs.AddEncodeSimpleMany(args.args) } + + if onLedger { + return s.postRequestOnLedger(hFuncName, reqArgs, bal, keyPair) + } + req := request.NewOffLedger(s.chainID, s.scHname, iscp.Hname(hFuncName), reqArgs) req.WithTransfer(bal) req.Sign(keyPair) @@ -75,6 +80,11 @@ func (s *Service) PostRequest(hFuncName uint32, args *Arguments, transfer *Trans return Request{id: &id} } +func (s *Service) postRequestOnLedger(hFuncName uint32, args requestargs.RequestArgs, bal colored.Balances, pair *ed25519.KeyPair) Request { + // TODO implement + return Request{} +} + // overrides default contract name func (s *Service) ServiceContractName(contractName string) { s.scHname = iscp.Hn(contractName) diff --git a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts index 98b6fbf79d..06bb352dd9 100644 --- a/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts +++ b/packages/vm/wasmlib/ts/wasmclient/clientfunc.ts @@ -7,23 +7,30 @@ import { IKeyPair } from "./crypto"; export class ClientFunc { protected svc: wasmclient.Service; private keyPair: IKeyPair | null = null; + private onLedger: boolean = false; private xfer: wasmclient.Transfer = new wasmclient.Transfer(); constructor(svc: wasmclient.Service) { this.svc = svc; } + + // onLedger can override the default off-ledger to on-ledger posting + public onLedgerRequest(onLedger: boolean): void { + this.onLedger = onLedger; + } + // Sends a request to the smart contract service // You can wait for the request to complete by using the returned RequestID // as parameter to Service.waitRequest() - public async post(hFuncName: wasmclient.Hname, args: wasmclient.Arguments | null, offLedger: boolean = true): Promise { + public async post(hFuncName: wasmclient.Hname, args: wasmclient.Arguments | null): Promise { if (!args) args = new wasmclient.Arguments(); if (!this.keyPair) this.keyPair = this.svc.keyPair; if (!this.keyPair) throw new Error("Key pair not defined"); - return await this.svc.postRequest(hFuncName, args, this.xfer, this.keyPair, offLedger); + return await this.svc.postRequest(hFuncName, args, this.xfer, this.keyPair, this.onLedger); } // Optionally overrides the default keypair from the service diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 1e03518d59..2f3e2a713b 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -39,24 +39,26 @@ export class Service { args: wasmclient.Arguments, transfer: wasmclient.Transfer, keyPair: IKeyPair, - offLedger: boolean + onLedger: boolean ): Promise { const chainId = this.serviceClient.configuration.chainId; - if (offLedger) { + if (! onLedger) { + // requested off-ledger request const requestID = await this.serviceClient.waspClient.postOffLedgerRequest(chainId, this.scHname, hFuncName, args, transfer, keyPair); return requestID; - } else { - const payload: IOnLedger = { - contract: this.scHname, - entrypoint: hFuncName, - //TODO: map args - //arguments : args - }; - const transferAmount = transfer.get(Colors.IOTA_COLOR); - const transactionID = await this.serviceClient.goShimmerClient.postOnLedgerRequest(chainId, payload, transferAmount, keyPair); - if (!transactionID) throw new Error("No transaction id"); - return transactionID; } + + // requested on-ledger request + const payload: IOnLedger = { + contract: this.scHname, + entrypoint: hFuncName, + //TODO: map args + //arguments : args + }; + const transferAmount = transfer.get(Colors.IOTA_COLOR); + const transactionID = await this.serviceClient.goShimmerClient.postOnLedgerRequest(chainId, payload, transferAmount, keyPair); + if (!transactionID) throw new Error("No transaction id"); + return transactionID; } // overrides default contract name From 1e99c3ddc14a77dbd0cc23ede0180587e457313d Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Sat, 8 Jan 2022 00:33:31 +0100 Subject: [PATCH 074/120] set call to deposit function to be on-ledger. --- packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts index 0b534d01d3..92f8861212 100644 --- a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts @@ -177,6 +177,7 @@ export class GoShimmerClient { //depositfunc.agentID(destinationAgentID); depositfunc.transfer(Transfer.iotas(amount)); depositfunc.sign(keypair); + depositfunc.onLedgerRequest(true); await depositfunc.post(); } } From dbec8465b964578bced46a2cebf1824098f6a7c0 Mon Sep 17 00:00:00 2001 From: shawkyz Date: Sun, 9 Jan 2022 04:53:49 +0100 Subject: [PATCH 075/120] convert address to AgentID type --- .../wasmlib/ts/wasmclient/crypto/ed25519.ts | 19 +++++++++++++++++-- .../wasmclient/goshimmer/goshimmerclient.ts | 7 +++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts index bcb5e48ddd..f4e58ac567 100644 --- a/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts +++ b/packages/vm/wasmlib/ts/wasmclient/crypto/ed25519.ts @@ -1,5 +1,6 @@ -import nacl from 'tweetnacl'; -import { Buffer } from '../buffer'; +import nacl from "tweetnacl"; +import { AgentID } from ".."; +import { Buffer } from "../buffer"; import { Base58 } from "./base58"; import { Hash } from "./hash"; import { Seed } from "./seed"; @@ -9,6 +10,20 @@ export interface IKeyPair { secretKey: Buffer; } +/** + * Calculates the AgentID for the key pair's address. + * @param keyPair The key pair used to get the address and calculate the AgentID. + * @returns AgentID. + */ + export function getAgentId(keyPair: IKeyPair) : AgentID { + const address = getAddress(keyPair); + const addressBuffer = Base58.decode(address); + const hNameBuffer = Buffer.alloc(4); + const agentIdBuffer = Buffer.concat([addressBuffer,hNameBuffer]); + const agentId = Base58.encode(agentIdBuffer); + return agentId; +} + /** * Calculates the address for key pair * @param keyPair The key pair used to calculate the address diff --git a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts index 92f8861212..48aeb971e4 100644 --- a/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/goshimmer/goshimmerclient.ts @@ -16,7 +16,7 @@ import { IOnLedger, OnLedgerHelper } from "./models/on_ledger"; import { ISendTransactionRequest, ISendTransactionResponse, ITransaction, Transaction } from "./models/transaction"; import { Wallet } from "./wallet/wallet"; import { Colors } from "../colors"; -import { Configuration, Transfer } from ".."; +import { AgentID, Configuration, Transfer } from ".."; import { CoreAccountsService } from "../coreaccounts/service"; interface GoShimmerClientConfiguration { @@ -171,10 +171,9 @@ export class GoShimmerClient { ); } - public async depositIOTAToAccountInChain(keypair: IKeyPair, amount: bigint) { + public async depositIOTAToAccountInChain(keypair: IKeyPair, destinationAgentID: AgentID, amount: bigint) { const depositfunc = this.coreAccountsService.deposit(); - // TODO: add support for depositing into accounts other than the caller's - //depositfunc.agentID(destinationAgentID); + depositfunc.agentID(destinationAgentID); depositfunc.transfer(Transfer.iotas(amount)); depositfunc.sign(keypair); depositfunc.onLedgerRequest(true); From 5b73924bf7cb394aecf4697d9b51eb26d8a1b93a Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sat, 8 Jan 2022 21:09:25 -0800 Subject: [PATCH 076/120] Initial fix for core contracts --- contracts/wasm/Cargo.lock | 43 +++++++++++++------ .../go/wasmclient/coreaccounts/service.go | 24 ++++++++--- .../wasmlib/go/wasmclient/coreblob/service.go | 17 ++++++-- .../wasmlib/go/wasmclient/coreroot/service.go | 18 +++++--- packages/vm/wasmlib/go/wasmclient/results.go | 7 +++ .../ts/wasmclient/coreaccounts/service.ts | 24 ++++++++--- .../wasmlib/ts/wasmclient/coreblob/service.ts | 16 +++++-- .../wasmlib/ts/wasmclient/coreroot/service.ts | 8 +++- packages/vm/wasmlib/ts/wasmclient/results.ts | 11 ++++- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 8 ++-- .../generator/goclienttemplates/service.go | 15 +++++++ .../generator/tsclienttemplates/service.go | 15 +++++++ 12 files changed, 160 insertions(+), 46 deletions(-) diff --git a/contracts/wasm/Cargo.lock b/contracts/wasm/Cargo.lock index 291995c168..e74c9b7bb0 100644 --- a/contracts/wasm/Cargo.lock +++ b/contracts/wasm/Cargo.lock @@ -34,7 +34,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -44,7 +44,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -54,7 +54,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -64,7 +64,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -74,7 +74,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -84,7 +84,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -94,7 +94,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -104,7 +104,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -144,6 +144,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" +[[package]] +name = "myworld" +version = "0.1.0" +dependencies = [ + "console_error_panic_hook", + "wasm-bindgen-test", + "wasmlib 0.1.0 (git+https://github.com/iotaledger/wasp?branch=develop)", + "wee_alloc", +] + [[package]] name = "proc-macro2" version = "1.0.24" @@ -185,7 +195,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -195,7 +205,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -205,7 +215,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -215,7 +225,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib", + "wasmlib 0.1.0", "wee_alloc", ] @@ -323,6 +333,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wasmlib" +version = "0.1.0" +source = "git+https://github.com/iotaledger/wasp?branch=develop#afdb52b8ef315c582beddaf0730ece33b54f35e9" +dependencies = [ + "console_error_panic_hook", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.47" diff --git a/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go b/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go index 957a391ee5..fb343bcad0 100644 --- a/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go @@ -78,8 +78,12 @@ type AccountsResults struct { res wasmclient.Results } -func (r *AccountsResults) Agents() []byte { - return r.res.GetBytes(ResAgents) +func (r *AccountsResults) Agents() map[wasmclient.AgentID][]byte { + res := make(map[wasmclient.AgentID][]byte) + r.res.ForEach(func(key string, val string) { + res[wasmclient.AgentID(key)] = r.res.GetBytes(val) + }) + return res } ///////////////////////////// balance ///////////////////////////// @@ -103,8 +107,12 @@ type BalanceResults struct { res wasmclient.Results } -func (r *BalanceResults) Balances() int64 { - return r.res.GetInt64(ResBalances) +func (r *BalanceResults) Balances() map[wasmclient.Color]int64 { + res := make(map[wasmclient.Color]int64) + r.res.ForEach(func(key string, val string) { + res[wasmclient.Color(key)] = r.res.GetInt64(val) + }) + return res } ///////////////////////////// getAccountNonce ///////////////////////////// @@ -147,8 +155,12 @@ type TotalAssetsResults struct { res wasmclient.Results } -func (r *TotalAssetsResults) Balances() int64 { - return r.res.GetInt64(ResBalances) +func (r *TotalAssetsResults) Balances() map[wasmclient.Color]int64 { + res := make(map[wasmclient.Color]int64) + r.res.ForEach(func(key string, val string) { + res[wasmclient.Color(key)] = r.res.GetInt64(val) + }) + return res } ///////////////////////////// CoreAccountsService ///////////////////////////// diff --git a/packages/vm/wasmlib/go/wasmclient/coreblob/service.go b/packages/vm/wasmlib/go/wasmclient/coreblob/service.go index 24fa9d1236..328233ac8e 100644 --- a/packages/vm/wasmlib/go/wasmclient/coreblob/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coreblob/service.go @@ -5,6 +5,7 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead +//nolint:unconvert package coreblobclient import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" @@ -85,8 +86,12 @@ type GetBlobInfoResults struct { res wasmclient.Results } -func (r *GetBlobInfoResults) BlobSizes() int32 { - return r.res.GetInt32(ResBlobSizes) +func (r *GetBlobInfoResults) BlobSizes() map[string]int32 { + res := make(map[string]int32) + r.res.ForEach(func(key string, val string) { + res[string(key)] = r.res.GetInt32(val) + }) + return res } ///////////////////////////// listBlobs ///////////////////////////// @@ -104,8 +109,12 @@ type ListBlobsResults struct { res wasmclient.Results } -func (r *ListBlobsResults) BlobSizes() int32 { - return r.res.GetInt32(ResBlobSizes) +func (r *ListBlobsResults) BlobSizes() map[wasmclient.Hash]int32 { + res := make(map[wasmclient.Hash]int32) + r.res.ForEach(func(key string, val string) { + res[wasmclient.Hash(key)] = r.res.GetInt32(val) + }) + return res } ///////////////////////////// CoreBlobService ///////////////////////////// diff --git a/packages/vm/wasmlib/go/wasmclient/coreroot/service.go b/packages/vm/wasmlib/go/wasmclient/coreroot/service.go index 36fa99205a..a6ade78ed4 100644 --- a/packages/vm/wasmlib/go/wasmclient/coreroot/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coreroot/service.go @@ -10,14 +10,14 @@ package corerootclient import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" const ( - ArgDeployer = "dp" + ArgDeployer = "dp" ArgDescription = "ds" - ArgHname = "hn" - ArgName = "nm" + ArgHname = "hn" + ArgName = "nm" ArgProgramHash = "ph" - ResContractFound = "cf" - ResContractRecData = "dt" + ResContractFound = "cf" + ResContractRecData = "dt" ResContractRegistry = "r" ) @@ -122,8 +122,12 @@ type GetContractRecordsResults struct { res wasmclient.Results } -func (r *GetContractRecordsResults) ContractRegistry() []byte { - return r.res.GetBytes(ResContractRegistry) +func (r *GetContractRecordsResults) ContractRegistry() map[wasmclient.Hname][]byte { + res := make(map[wasmclient.Hname][]byte) + r.res.ForEach(func(key string, val string) { + res[wasmclient.Hname(key)] = r.res.GetBytes(val) + }) + return res } ///////////////////////////// CoreRootService ///////////////////////////// diff --git a/packages/vm/wasmlib/go/wasmclient/results.go b/packages/vm/wasmlib/go/wasmclient/results.go index 4f392cf39d..af3747737e 100644 --- a/packages/vm/wasmlib/go/wasmclient/results.go +++ b/packages/vm/wasmlib/go/wasmclient/results.go @@ -19,6 +19,13 @@ func (r Results) Exists(key string) bool { return ok } +func (r Results) ForEach(pair func(key string, val string)) { + r.res.ForEach(func(key kv.Key, value []byte) bool { + pair(Base58Encode([]byte(key)), string(key)) + return true + }) +} + func (r Results) get(key string, typeID int32) []byte { size := TypeSizes[typeID] bytes, ok := r.res[kv.Key(key)] diff --git a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts index 65c281eaa4..9d06ab363a 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts @@ -67,8 +67,12 @@ export class AccountsView extends wasmclient.ClientView { export class AccountsResults extends wasmclient.ViewResults { - agents(): wasmclient.Bytes { - return this.res.getBytes(ResAgents); + agents(): Map { + let res = new Map(); + this.res.forEach((key, val) => { + res.set(key, this.res.getBytes(val)); + }); + return res; } } @@ -89,8 +93,12 @@ export class BalanceView extends wasmclient.ClientView { export class BalanceResults extends wasmclient.ViewResults { - balances(): wasmclient.Int64 { - return this.res.getInt64(ResBalances); + balances(): Map { + let res = new Map(); + this.res.forEach((key, val) => { + res.set(key, this.res.getInt64(val)); + }); + return res; } } @@ -127,8 +135,12 @@ export class TotalAssetsView extends wasmclient.ClientView { export class TotalAssetsResults extends wasmclient.ViewResults { - balances(): wasmclient.Int64 { - return this.res.getInt64(ResBalances); + balances(): Map { + let res = new Map(); + this.res.forEach((key, val) => { + res.set(key, this.res.getInt64(val)); + }); + return res; } } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts index 5edd7751ef..df8c61be7c 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts @@ -80,8 +80,12 @@ export class GetBlobInfoView extends wasmclient.ClientView { export class GetBlobInfoResults extends wasmclient.ViewResults { - blobSizes(): wasmclient.Int32 { - return this.res.getInt32(ResBlobSizes); + blobSizes(): Map { + let res = new Map(); + this.res.forEach((key, val) => { + res.set(key, this.res.getInt32(val)); + }); + return res; } } @@ -96,8 +100,12 @@ export class ListBlobsView extends wasmclient.ClientView { export class ListBlobsResults extends wasmclient.ViewResults { - blobSizes(): wasmclient.Int32 { - return this.res.getInt32(ResBlobSizes); + blobSizes(): Map { + let res = new Map(); + this.res.forEach((key, val) => { + res.set(key, this.res.getInt32(val)); + }); + return res; } } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts index 470ba275f5..ebb09a7185 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts @@ -108,8 +108,12 @@ export class GetContractRecordsView extends wasmclient.ClientView { export class GetContractRecordsResults extends wasmclient.ViewResults { - contractRegistry(): wasmclient.Bytes { - return this.res.getBytes(ResContractRegistry); + contractRegistry(): Map { + let res = new Map(); + this.res.forEach((key, val) => { + res.set(key, this.res.getBytes(val)); + }); + return res; } } diff --git a/packages/vm/wasmlib/ts/wasmclient/results.ts b/packages/vm/wasmlib/ts/wasmclient/results.ts index dc90d5f173..b39e0d40b3 100644 --- a/packages/vm/wasmlib/ts/wasmclient/results.ts +++ b/packages/vm/wasmlib/ts/wasmclient/results.ts @@ -14,11 +14,18 @@ export class ViewResults { export class Results { res = new Map(); + keys = new Map(); exists(key: string): wasmclient.Bool { return this.res.has(key); } + forEach(callbackfn: (base58Key: string, valueKey: string) => void): void { + this.keys.forEach((val, key, map) => { + callbackfn(Base58.encode(val), key); + }) + } + private get(key: string, typeID: wasmclient.Int32): wasmclient.Bytes { const size = wasmclient.TYPE_SIZES[typeID]; const bytes = this.res.get(key); @@ -65,7 +72,7 @@ export class Results { } getHname(key: string): wasmclient.Hname { - return this.get(key, wasmclient.TYPE_HNAME).readUInt32LE(0); + return this.get(key, wasmclient.TYPE_HNAME).readUInt32LE(0); } getInt8(key: string): wasmclient.Int8 { @@ -108,5 +115,5 @@ export class Results { return this.get(key, wasmclient.TYPE_INT64).readBigUInt64LE(0); } - // TODO Decode() from view call response into map + // TODO Decode() from view call response into map } diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 822d0e59e0..6f69d21aeb 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -35,9 +35,11 @@ export class WaspClient { if (result?.body !== null && result.body.Items) { for (const item of result.body.Items) { - const key = Buffer.from(item.Key, "base64").toString(); - const value = Buffer.from(item.Value, "base64"); - res.res.set(key, value); + const key = Buffer.from(item.Key, "base64"); + const val = Buffer.from(item.Value, "base64"); + const stringKey = key.toString() + res.res.set(stringKey, val); + res.keys.set(stringKey, key); } } return res; diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index 41e0710b2c..be94d5e9a7 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -118,6 +118,21 @@ $#each result callResultGetter `, // ******************************* "callResultGetter": ` +$#if map callResultGetterMap callResultGetterBasic +`, + // ******************************* + "callResultGetterMap": ` + +func (r *$FuncName$+Results) $FldName() map[$fldKeyLangType]$fldLangType { + res := make(map[$fldKeyLangType]$fldLangType) + r.res.ForEach(func(key string, val string) { + res[$fldKeyLangType(key)] = r.res.Get$FldType(val) + }) + return res +} +`, + // ******************************* + "callResultGetterBasic": ` $#if mandatory else callResultOptional func (r *$FuncName$+Results) $FldName() $fldLangType { diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 86462a2823..57981185bb 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -111,6 +111,21 @@ $#each result callResultGetter `, // ******************************* "callResultGetter": ` +$#if map callResultGetterMap callResultGetterBasic +`, + // ******************************* + "callResultGetterMap": ` + + $fldName(): Map<$fldLangType, $fldKeyLangType> { + let res = new Map(); + this.res.forEach((key, val) => { + res.set(key, this.res.get$FldType(val)); + }); + return res; + } +`, + // ******************************* + "callResultGetterBasic": ` $#if mandatory else callResultOptional $fldName(): $fldLangType { From 695cbc194882549d9e557f30bab8afbc147f6ded Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sat, 8 Jan 2022 21:17:47 -0800 Subject: [PATCH 077/120] Fixed map paramaters for TS --- packages/vm/wasmlib/go/wasmclient/coreblob/service.go | 1 - packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts | 6 +++--- packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts | 4 ++-- packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts | 2 +- tools/schema/generator/tsclienttemplates/service.go | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/vm/wasmlib/go/wasmclient/coreblob/service.go b/packages/vm/wasmlib/go/wasmclient/coreblob/service.go index 328233ac8e..0e5bca9d5c 100644 --- a/packages/vm/wasmlib/go/wasmclient/coreblob/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coreblob/service.go @@ -5,7 +5,6 @@ // >>>> DO NOT CHANGE THIS FILE! <<<< // Change the json schema instead -//nolint:unconvert package coreblobclient import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" diff --git a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts index 9d06ab363a..462813b290 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts @@ -67,7 +67,7 @@ export class AccountsView extends wasmclient.ClientView { export class AccountsResults extends wasmclient.ViewResults { - agents(): Map { + agents(): Map { let res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getBytes(val)); @@ -93,7 +93,7 @@ export class BalanceView extends wasmclient.ClientView { export class BalanceResults extends wasmclient.ViewResults { - balances(): Map { + balances(): Map { let res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getInt64(val)); @@ -135,7 +135,7 @@ export class TotalAssetsView extends wasmclient.ClientView { export class TotalAssetsResults extends wasmclient.ViewResults { - balances(): Map { + balances(): Map { let res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getInt64(val)); diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts index df8c61be7c..fc331af695 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts @@ -80,7 +80,7 @@ export class GetBlobInfoView extends wasmclient.ClientView { export class GetBlobInfoResults extends wasmclient.ViewResults { - blobSizes(): Map { + blobSizes(): Map { let res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getInt32(val)); @@ -100,7 +100,7 @@ export class ListBlobsView extends wasmclient.ClientView { export class ListBlobsResults extends wasmclient.ViewResults { - blobSizes(): Map { + blobSizes(): Map { let res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getInt32(val)); diff --git a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts index ebb09a7185..6269b4a249 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts @@ -108,7 +108,7 @@ export class GetContractRecordsView extends wasmclient.ClientView { export class GetContractRecordsResults extends wasmclient.ViewResults { - contractRegistry(): Map { + contractRegistry(): Map { let res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getBytes(val)); diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 57981185bb..f9407b82a9 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -116,7 +116,7 @@ $#if map callResultGetterMap callResultGetterBasic // ******************************* "callResultGetterMap": ` - $fldName(): Map<$fldLangType, $fldKeyLangType> { + $fldName(): Map<$fldKeyLangType, $fldLangType> { let res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.get$FldType(val)); From ebc57fe871ce96a5fe779841cc7668ed2e64f7c1 Mon Sep 17 00:00:00 2001 From: shawkyz Date: Sun, 9 Jan 2022 06:49:57 +0100 Subject: [PATCH 078/120] fix endpoint path --- packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts b/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts index 378e5dd58b..63be3293f3 100644 --- a/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts +++ b/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts @@ -23,6 +23,8 @@ export async function sendRequestExt( let fetchResponse: Response; try { + if(!path.startsWith("/")) + path = "/" + path; const url = `${apiUrl}/${path}`; fetchResponse = await fetch(url, { method: verb, From d09aa4f8de4336eafcf4f7c7a6721b178a701aa0 Mon Sep 17 00:00:00 2001 From: shawkyz Date: Sun, 9 Jan 2022 06:50:34 +0100 Subject: [PATCH 079/120] send topics instead of msg to event --- packages/vm/wasmlib/ts/wasmclient/service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 2f3e2a713b..19880ec2fc 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -103,7 +103,7 @@ export class Service { const topic = topics[0]; if (this.eventHandlers && this.eventHandlers.has(topic)) { const eventHandler = this.eventHandlers.get(topic)!; - const eventHandlerMsg = msg.slice(1); + const eventHandlerMsg = topics.slice(1); eventHandler(eventHandlerMsg); } } From 5d41a39be343703cc8ecec327eb35d4c2a400b6c Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Sun, 9 Jan 2022 08:02:27 +0100 Subject: [PATCH 080/120] Removed unused variable --- packages/vm/wasmlib/ts/wasmclient/results.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/results.ts b/packages/vm/wasmlib/ts/wasmclient/results.ts index b39e0d40b3..d55a9527bb 100644 --- a/packages/vm/wasmlib/ts/wasmclient/results.ts +++ b/packages/vm/wasmlib/ts/wasmclient/results.ts @@ -21,7 +21,7 @@ export class Results { } forEach(callbackfn: (base58Key: string, valueKey: string) => void): void { - this.keys.forEach((val, key, map) => { + this.keys.forEach((val, key) => { callbackfn(Base58.encode(val), key); }) } From 6f52e4e3e84397ce50ea40d2d5b4af3f622d5408 Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Sun, 9 Jan 2022 08:03:03 +0100 Subject: [PATCH 081/120] Made core accounts contract strict compiler options compliant --- packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts | 6 +++--- tools/schema/generator/tsclienttemplates/service.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts index 462813b290..cc25b08aa0 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts @@ -68,7 +68,7 @@ export class AccountsView extends wasmclient.ClientView { export class AccountsResults extends wasmclient.ViewResults { agents(): Map { - let res = new Map(); + const res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getBytes(val)); }); @@ -94,7 +94,7 @@ export class BalanceView extends wasmclient.ClientView { export class BalanceResults extends wasmclient.ViewResults { balances(): Map { - let res = new Map(); + const res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getInt64(val)); }); @@ -136,7 +136,7 @@ export class TotalAssetsView extends wasmclient.ClientView { export class TotalAssetsResults extends wasmclient.ViewResults { balances(): Map { - let res = new Map(); + const res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getInt64(val)); }); diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index f9407b82a9..edb333b7c9 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -117,7 +117,7 @@ $#if map callResultGetterMap callResultGetterBasic "callResultGetterMap": ` $fldName(): Map<$fldKeyLangType, $fldLangType> { - let res = new Map(); + const res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.get$FldType(val)); }); From f31fc4b98647ae6d33a2b022041cb0a149d9131c Mon Sep 17 00:00:00 2001 From: "Bruno A. dos Santos" Date: Sun, 9 Jan 2022 08:31:50 +0100 Subject: [PATCH 082/120] fixed bug which caused empty responses to throw error --- packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts b/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts index 63be3293f3..4fba917538 100644 --- a/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts +++ b/packages/vm/wasmlib/ts/wasmclient/api_common/request_sender.ts @@ -37,7 +37,8 @@ export async function sendRequestExt( } try { - const response = await fetchResponse.json(); + const response = await fetchResponse.text() + .then((data)=> data ? JSON.parse(data) : {}); return { body: response, response: fetchResponse }; } catch (err) { const error = err as Error; From 0ad0cdc35967a7a95b9800e15c91a9f9a7bd6383 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sun, 9 Jan 2022 12:47:22 -0800 Subject: [PATCH 083/120] Correct argument encoding for calling view --- .../vm/wasmlib/ts/wasmclient/arguments.ts | 21 +++++++++++++++++++ packages/vm/wasmlib/ts/wasmclient/service.ts | 2 +- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 9 +++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 23b03be0a5..3c3d7ba3ec 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -163,4 +163,25 @@ export class Arguments { } return buf; } + +// {"Items":[{"Key":"YQ==","Value":"AHDllHMJmeQf4m2jKzi+S+sLkLlpZuSJy5lpIvaJ8JN2AAAAAA=="}]} + + encodeCall(): wasmclient.Items { + const keys = new Array(); + for (const key of this.args.keys()) { + keys.push(key); + } + keys.sort((lhs, rhs) => lhs.localeCompare(rhs)); + + let items = new wasmclient.Items() + for (const key of keys) { + const keyBuf = Buffer.from("-" + key); + const valBuf = this.args.get(key); + if (!valBuf) { + throw new Error("Arguments.encodeCall: missing value"); + } + items.Items.push({ keyBuf.toString("base64"), valBuf.toString("base64") }) + } + return items; + } } diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 19880ec2fc..28a5fb7a5b 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -30,7 +30,7 @@ export class Service { this.serviceClient.configuration.chainId, this.scHname.toString(16), viewName, - args.encode() + args.encodeCall() ); } diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 6f69d21aeb..66e5b35540 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -15,6 +15,10 @@ interface IOffLedgerRequest { Request: string; } +interface Items { + Items: [{ Key: string; Value: string }]; +} + export class WaspClient { private waspAPI: string; @@ -23,13 +27,12 @@ export class WaspClient { else this.waspAPI = "http://" + waspAPI; } - public async callView(chainID: string, contractHName: string, entryPoint: string, args: Buffer): Promise { - const request = { Request: args.toString("base64") }; + public async callView(chainID: string, contractHName: string, entryPoint: string, args: Items): Promise { const result = await requestSender.sendRequestExt( this.waspAPI, "post", `/chain/${chainID}/contract/${contractHName}/callview/${entryPoint}`, - request + args ); const res = new wasmclient.Results(); From 55bf9882abe325d4f5ba9964f62036c72fd9ec02 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Sun, 9 Jan 2022 17:42:33 -0800 Subject: [PATCH 084/120] Simplified argument conversion --- client/callview.go | 2 +- contracts/wasm/Cargo.lock | 43 +-- .../test/testwasmlib_client_test.go | 25 +- packages/vm/wasmlib/go/wasmclient/seed.go | 11 + .../vm/wasmlib/ts/wasmclient/arguments.ts | 324 +++++++++--------- .../wasmlib/ts/wasmclient/coreblob/service.ts | 4 +- .../wasmlib/ts/wasmclient/coreroot/service.ts | 2 +- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 14 +- 8 files changed, 219 insertions(+), 206 deletions(-) diff --git a/client/callview.go b/client/callview.go index c64b5b45eb..192e16f8b3 100644 --- a/client/callview.go +++ b/client/callview.go @@ -28,7 +28,7 @@ func (c *WaspClient) CallView(chainID *iscp.ChainID, hContract iscp.Hname, funct var res dict.Dict var err error for { - err = c.do(http.MethodGet, routes.CallView(chainID.Base58(), hContract.String(), functionName), arguments, &res) + err = c.do(http.MethodPost, routes.CallView(chainID.Base58(), hContract.String(), functionName), arguments, &res) switch { case err == nil: return res, err diff --git a/contracts/wasm/Cargo.lock b/contracts/wasm/Cargo.lock index e74c9b7bb0..291995c168 100644 --- a/contracts/wasm/Cargo.lock +++ b/contracts/wasm/Cargo.lock @@ -34,7 +34,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -44,7 +44,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -54,7 +54,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -64,7 +64,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -74,7 +74,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -84,7 +84,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -94,7 +94,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -104,7 +104,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -144,16 +144,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" -[[package]] -name = "myworld" -version = "0.1.0" -dependencies = [ - "console_error_panic_hook", - "wasm-bindgen-test", - "wasmlib 0.1.0 (git+https://github.com/iotaledger/wasp?branch=develop)", - "wee_alloc", -] - [[package]] name = "proc-macro2" version = "1.0.24" @@ -195,7 +185,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -205,7 +195,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -215,7 +205,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -225,7 +215,7 @@ version = "0.1.0" dependencies = [ "console_error_panic_hook", "wasm-bindgen-test", - "wasmlib 0.1.0", + "wasmlib", "wee_alloc", ] @@ -333,15 +323,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "wasmlib" -version = "0.1.0" -source = "git+https://github.com/iotaledger/wasp?branch=develop#afdb52b8ef315c582beddaf0730ece33b54f35e9" -dependencies = [ - "console_error_panic_hook", - "wasm-bindgen", -] - [[package]] name = "web-sys" version = "0.3.47" diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go index 734e364c1f..0555b0c90a 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go @@ -6,6 +6,7 @@ import ( "github.com/iotaledger/wasp/contracts/wasm/testwasmlib/go/testwasmlibclient" "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" + coreaccountsclient "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient/coreaccounts" "github.com/stretchr/testify/require" ) @@ -15,12 +16,12 @@ import ( // these values are usually available from elsewhere const ( mySeed = "6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv" - myChainID = "jn52vSuUUYY22T1mV2ny14EADYBu3ofyewLRSsVRnjpz" + myChainID = "j2mrizabjDRPwU2eU37yXFLcgCHCwVUD79v2ufWbBFm4" ) func setupClient(t *testing.T) *testwasmlibclient.TestWasmLibService { // for now skip client tests - t.SkipNow() + // t.SkipNow() require.True(t, wasmclient.SeedIsValid(mySeed)) require.True(t, wasmclient.ChainIsValid(myChainID)) @@ -117,3 +118,23 @@ func TestClientArray(t *testing.T) { require.NoError(t, v.Error()) require.EqualValues(t, 0, res.Length()) } + +func TestAccountBalance(t *testing.T) { + // we're testing against wasp-cluster, so defaults will do + svcClient := wasmclient.DefaultServiceClient() + + // create the service for the testwasmlib smart contract + svc, err := coreaccountsclient.NewCoreAccountsService(svcClient, myChainID) + require.NoError(t, err) + + // we'll use the first address in the seed to sign requests + svc.SignRequests(wasmclient.SeedToKeyPair(mySeed, 0)) + + bal := svc.Balance() + agendID := wasmclient.SeedToAgentID(mySeed, 0) + bal.AgentID(agendID) + res := bal.Call() + require.NoError(t, bal.Error()) + balances := res.Balances() + fmt.Printf("Balances: %v\n", balances) +} diff --git a/packages/vm/wasmlib/go/wasmclient/seed.go b/packages/vm/wasmlib/go/wasmclient/seed.go index 971bc858f0..6c5cc8a7c4 100644 --- a/packages/vm/wasmlib/go/wasmclient/seed.go +++ b/packages/vm/wasmlib/go/wasmclient/seed.go @@ -6,6 +6,7 @@ package wasmclient import ( "github.com/iotaledger/goshimmer/client/wallet/packages/seed" "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/wasp/packages/iscp" "github.com/mr-tron/base58" ) @@ -28,6 +29,16 @@ func SeedToAddress(mySeed string, index uint64) Address { return Address(base58.Encode(address.Address().Bytes())) } +func SeedToAgentID(mySeed string, index uint64) AgentID { + seedBytes, err := base58.Decode(mySeed) + if err != nil { + panic(err) + } + address := seed.NewSeed(seedBytes).Address(index) + agentID := iscp.NewAgentID(address.Address(), 0) + return AgentID(base58.Encode(agentID.Bytes())) +} + func SeedToKeyPair(mySeed string, index uint64) *ed25519.KeyPair { seedBytes, err := base58.Decode(mySeed) if err != nil { diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 3c3d7ba3ec..dcce6a43a6 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -8,180 +8,170 @@ import {Buffer} from "./buffer"; // The Arguments struct is used to gather all arguments for this smart // contract function call and encode it into this deterministic byte array export class Arguments { - private args = new Map(); + private args = new Map(); - get(key: string): wasmclient.Bytes { + get(key: string): wasmclient.Bytes { const bytes = this.args.get(key); return bytes ?? Buffer.alloc(0); } - private set(key: string, val: wasmclient.Bytes): void { - this.args.set(key, val); - } - - private setBase58(key: string, val: string, typeID: wasmclient.Int32): void { - const bytes = Base58.decode(val); - if (bytes.length != wasmclient.TYPE_SIZES[typeID]) { - wasmclient.panic("invalid byte size"); - } - this.set(key, bytes); - } - - indexedKey(key: string, index: wasmclient.Int32): string { - return key + "." + index.toString(); - } - - mandatory(key: string): void { - if (!this.args.has(key)) { - wasmclient.panic("missing mandatory " + key) - } - } - - setAddress(key: string, val: wasmclient.AgentID): void { - this.setBase58(key, val, wasmclient.TYPE_ADDRESS); - } - - setAgentID(key: string, val: wasmclient.AgentID): void { - this.setBase58(key, val, wasmclient.TYPE_AGENT_ID); - } - - setBool(key: string, val: boolean): void { - const bytes = Buffer.alloc(1); - if (val) { - bytes.writeUInt8(1, 0); - } - this.set(key, bytes) - } - - setBytes(key: string, val: wasmclient.Bytes): void { - this.set(key, Buffer.from(val)); - } - - setColor(key: string, val: wasmclient.Color): void { - this.setBase58(key, val, wasmclient.TYPE_COLOR); - } - - setChainID(key: string, val: wasmclient.ChainID): void { - this.setBase58(key, val, wasmclient.TYPE_CHAIN_ID); - } - - setHash(key: string, val: wasmclient.Hash): void { - this.setBase58(key, val, wasmclient.TYPE_HASH); - } - - setHname(key: string, val: wasmclient.Hname): void { - this.setUint32(key, val); - } - - setInt8(key: string, val: wasmclient.Int8): void { - const bytes = Buffer.alloc(1); - bytes.writeInt8(val, 0); - this.set(key, bytes); - } - - setInt16(key: string, val: wasmclient.Int16): void { - const bytes = Buffer.alloc(2); - bytes.writeInt16LE(val, 0); - this.set(key, bytes); - } - - setInt32(key: string, val: wasmclient.Int32): void { - const bytes = Buffer.alloc(4); - bytes.writeInt32LE(val, 0); - this.set(key, bytes); - } - - setInt64(key: string, val: wasmclient.Int64): void { - const bytes = Buffer.alloc(8); - bytes.writeBigInt64LE(val, 0); - this.set(key, bytes); - } - - setRequestID(key: string, val: wasmclient.RequestID): void { - this.setBase58(key, val, wasmclient.TYPE_REQUEST_ID); - } - - getString(key: string): string { + private set(key: string, val: wasmclient.Bytes): void { + this.args.set(key, val); + } + + private setBase58(key: string, val: string, typeID: wasmclient.Int32): void { + const bytes = Base58.decode(val); + if (bytes.length != wasmclient.TYPE_SIZES[typeID]) { + wasmclient.panic("invalid byte size"); + } + this.set(key, bytes); + } + + indexedKey(key: string, index: wasmclient.Int32): string { + return key + "." + index.toString(); + } + + mandatory(key: string): void { + if (!this.args.has(key)) { + wasmclient.panic("missing mandatory " + key) + } + } + + setAddress(key: string, val: wasmclient.AgentID): void { + this.setBase58(key, val, wasmclient.TYPE_ADDRESS); + } + + setAgentID(key: string, val: wasmclient.AgentID): void { + this.setBase58(key, val, wasmclient.TYPE_AGENT_ID); + } + + setBool(key: string, val: boolean): void { + const bytes = Buffer.alloc(1); + if (val) { + bytes.writeUInt8(1, 0); + } + this.set(key, bytes) + } + + setBytes(key: string, val: wasmclient.Bytes): void { + this.set(key, Buffer.from(val)); + } + + setColor(key: string, val: wasmclient.Color): void { + this.setBase58(key, val, wasmclient.TYPE_COLOR); + } + + setChainID(key: string, val: wasmclient.ChainID): void { + this.setBase58(key, val, wasmclient.TYPE_CHAIN_ID); + } + + setHash(key: string, val: wasmclient.Hash): void { + this.setBase58(key, val, wasmclient.TYPE_HASH); + } + + setHname(key: string, val: wasmclient.Hname): void { + this.setUint32(key, val); + } + + setInt8(key: string, val: wasmclient.Int8): void { + const bytes = Buffer.alloc(1); + bytes.writeInt8(val, 0); + this.set(key, bytes); + } + + setInt16(key: string, val: wasmclient.Int16): void { + const bytes = Buffer.alloc(2); + bytes.writeInt16LE(val, 0); + this.set(key, bytes); + } + + setInt32(key: string, val: wasmclient.Int32): void { + const bytes = Buffer.alloc(4); + bytes.writeInt32LE(val, 0); + this.set(key, bytes); + } + + setInt64(key: string, val: wasmclient.Int64): void { + const bytes = Buffer.alloc(8); + bytes.writeBigInt64LE(val, 0); + this.set(key, bytes); + } + + setRequestID(key: string, val: wasmclient.RequestID): void { + this.setBase58(key, val, wasmclient.TYPE_REQUEST_ID); + } + + getString(key: string): string { const bytes = this.get(key); return bytes.toString(); } - setString(key: string, val: string): void { - this.set(key, Buffer.from(val)); - } - - setUint8(key: string, val: wasmclient.Uint8): void { - const bytes = Buffer.alloc(1); - bytes.writeUInt8(val, 0); - this.set(key, bytes); - } - - setUint16(key: string, val: wasmclient.Uint16): void { - const bytes = Buffer.alloc(2); - bytes.writeUInt16LE(val, 0); - this.set(key, bytes); - } - - setUint32(key: string, val: wasmclient.Uint32): void { - const bytes = Buffer.alloc(4); - bytes.writeUInt32LE(val, 0); - this.set(key, bytes); - } - - setUint64(key: string, val: wasmclient.Uint64): void { - const bytes = Buffer.alloc(8); - bytes.writeBigUInt64LE(val, 0); - this.set(key, bytes); - } - - // Encode returns a byte array that encodes the Arguments as follows: - // Sort all keys in ascending order (very important, because this data - // will be part of the data that will be signed, so the order needs to - // be 100% deterministic). Then emit the 4-byte argument count. - // Next for each argument emit the 2-byte key length, the key prepended - // with the minus sign, the 4-byte value length, and then the value bytes. - encode(): wasmclient.Bytes { - const keys = new Array(); - for (const key of this.args.keys()) { - keys.push(key); - } - keys.sort((lhs, rhs) => lhs.localeCompare(rhs)); - - let buf = Buffer.alloc(4); - buf.writeUInt32LE(keys.length, 0); - for (const key of keys) { - const keyBuf = Buffer.from("-" + key); - const keyLen = Buffer.alloc(2); - keyLen.writeUInt16LE(keyBuf.length, 0); - const valBuf = this.args.get(key); - if (!valBuf) { - throw new Error("Arguments.encode: missing value"); - } - const valLen = Buffer.alloc(4); - valLen.writeUInt32LE(valBuf.length, 0); - buf = Buffer.concat([buf, keyLen, keyBuf, valLen, valBuf]); - } - return buf; - } - -// {"Items":[{"Key":"YQ==","Value":"AHDllHMJmeQf4m2jKzi+S+sLkLlpZuSJy5lpIvaJ8JN2AAAAAA=="}]} - - encodeCall(): wasmclient.Items { - const keys = new Array(); - for (const key of this.args.keys()) { - keys.push(key); - } - keys.sort((lhs, rhs) => lhs.localeCompare(rhs)); - - let items = new wasmclient.Items() - for (const key of keys) { - const keyBuf = Buffer.from("-" + key); - const valBuf = this.args.get(key); - if (!valBuf) { - throw new Error("Arguments.encodeCall: missing value"); - } - items.Items.push({ keyBuf.toString("base64"), valBuf.toString("base64") }) - } - return items; - } + setString(key: string, val: string): void { + this.set(key, Buffer.from(val)); + } + + setUint8(key: string, val: wasmclient.Uint8): void { + const bytes = Buffer.alloc(1); + bytes.writeUInt8(val, 0); + this.set(key, bytes); + } + + setUint16(key: string, val: wasmclient.Uint16): void { + const bytes = Buffer.alloc(2); + bytes.writeUInt16LE(val, 0); + this.set(key, bytes); + } + + setUint32(key: string, val: wasmclient.Uint32): void { + const bytes = Buffer.alloc(4); + bytes.writeUInt32LE(val, 0); + this.set(key, bytes); + } + + setUint64(key: string, val: wasmclient.Uint64): void { + const bytes = Buffer.alloc(8); + bytes.writeBigUInt64LE(val, 0); + this.set(key, bytes); + } + + // Encode returns a byte array that encodes the Arguments as follows: + // Sort all keys in ascending order (very important, because this data + // will be part of the data that will be signed, so the order needs to + // be 100% deterministic). Then emit the 4-byte argument count. + // Next for each argument emit the 2-byte key length, the key prepended + // with the minus sign, the 4-byte value length, and then the value bytes. + encode(): wasmclient.Bytes { + const keys = new Array(); + for (const key of this.args.keys()) { + keys.push(key); + } + keys.sort((lhs, rhs) => lhs.localeCompare(rhs)); + + let buf = Buffer.alloc(4); + buf.writeUInt32LE(keys.length, 0); + for (const key of keys) { + const keyBuf = Buffer.from("-" + key); + const keyLen = Buffer.alloc(2); + keyLen.writeUInt16LE(keyBuf.length, 0); + const valBuf = this.args.get(key); + if (!valBuf) { + throw new Error("Arguments.encode: missing value"); + } + const valLen = Buffer.alloc(4); + valLen.writeUInt32LE(valBuf.length, 0); + buf = Buffer.concat([buf, keyLen, keyBuf, valLen, valBuf]); + } + return buf; + } + + // {"Items":[{"Key":"YQ==","Value":"AHDllHMJmeQf4m2jKzi+S+sLkLlpZuSJy5lpIvaJ8JN2AAAAAA=="}]} + encodeCall(): wasmclient.Items { + let items = new wasmclient.Items() + for (const [key, val] of this.args) { + const k = Buffer.from("-" + key).toString("base64"); + const v = val.toString("base64"); + items.Items.push(new wasmclient.Item(k, v)) + } + return items; + } } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts index fc331af695..0302c74563 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts @@ -81,7 +81,7 @@ export class GetBlobInfoView extends wasmclient.ClientView { export class GetBlobInfoResults extends wasmclient.ViewResults { blobSizes(): Map { - let res = new Map(); + const res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getInt32(val)); }); @@ -101,7 +101,7 @@ export class ListBlobsView extends wasmclient.ClientView { export class ListBlobsResults extends wasmclient.ViewResults { blobSizes(): Map { - let res = new Map(); + const res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getInt32(val)); }); diff --git a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts index 6269b4a249..2009f5dfd4 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts @@ -109,7 +109,7 @@ export class GetContractRecordsView extends wasmclient.ClientView { export class GetContractRecordsResults extends wasmclient.ViewResults { contractRegistry(): Map { - let res = new Map(); + const res = new Map(); this.res.forEach((key, val) => { res.set(key, this.res.getBytes(val)); }); diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 66e5b35540..33d2633529 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -15,8 +15,18 @@ interface IOffLedgerRequest { Request: string; } -interface Items { - Items: [{ Key: string; Value: string }]; +export class Item { + Key: string; + Value: string; + + constructor(key: string, value: string) { + this.Key = key; + this.Value = value; + } +} + +export class Items { + Items = new Array(); } export class WaspClient { From 980f9dc98fdbab69203a3492040258297abe1653 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Mon, 10 Jan 2022 18:35:45 +0200 Subject: [PATCH 085/120] The tests are passing. --- packages/chain/chainimpl/chainimpl.go | 37 +++++++++---------- tools/cluster/cluster.go | 12 +----- tools/cluster/tests/account_test.go | 30 ++++++++------- .../cluster/tests/advanced_inccounter_test.go | 16 ++------ tools/cluster/tests/transfer_test.go | 9 +++-- tools/cluster/tests/util.go | 2 +- 6 files changed, 43 insertions(+), 63 deletions(-) diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index 67ec227cce..9eaecdd551 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -268,7 +268,7 @@ func (c *chainObj) processChainTransition(msg *chain.ChainTransitionEventData) { chain.LogStateTransition(msg, reqids, c.log) c.mempoolLastCleanedIndex = stateIndex - c.updateChainNodes() + c.updateChainNodes(stateIndex) } else { c.log.Debugf("processChainTransition state %d: output %s is governance updated; state hash %s", stateIndex, iscp.OID(msg.ChainOutput.ID()), msg.VirtualState.StateCommitment().String()) @@ -283,27 +283,22 @@ func (c *chainObj) processChainTransition(msg *chain.ChainTransitionEventData) { c.log.Debugf("processChainTransition completed: state index: %d, state hash: %s", stateIndex, msg.VirtualState.StateCommitment().String()) } -func (c *chainObj) updateChainNodes() { +func (c *chainObj) updateChainNodes(stateIndex uint32) { + c.log.Debugf("updateChainNodes, stateIndex=%v", stateIndex) govAccessNodes := make([]ed25519.PublicKey, 0) govCandidateNodes := make([]*governance.AccessNodeInfo, 0) - if c.consensus != nil { - statusSnapshot := c.consensus.GetStatusSnapshot() - if statusSnapshot != nil { - stateIndex := c.consensus.GetStatusSnapshot().StateIndex - if stateIndex > 0 { - res, err := viewcontext.NewFromChain(c).CallView( - governance.Contract.Hname(), - governance.FuncGetChainNodes.Hname(), - governance.GetChainNodesRequest{}.AsDict(), - ) - if err != nil { - c.log.Panicf("unable to read the governance contract state: %v", err) - } - govResponse := governance.NewGetChainNodesResponseFromDict(res) - govAccessNodes = govResponse.AccessNodes - govCandidateNodes = govResponse.AccessNodeCandidates - } + if stateIndex > 0 { + res, err := viewcontext.NewFromChain(c).CallView( + governance.Contract.Hname(), + governance.FuncGetChainNodes.Hname(), + governance.GetChainNodesRequest{}.AsDict(), + ) + if err != nil { + c.log.Panicf("unable to read the governance contract state: %v", err) } + govResponse := governance.NewGetChainNodesResponseFromDict(res) + govAccessNodes = govResponse.AccessNodes + govCandidateNodes = govResponse.AccessNodeCandidates } // @@ -374,5 +369,7 @@ func (c *chainObj) setCommittee(cmt chain.Committee) { cmt: cmt, }) } - c.updateChainNodes() + if stateIndex, err := c.stateReader.BlockIndex(); err != nil { + c.updateChainNodes(stateIndex) + } } diff --git a/tools/cluster/cluster.go b/tools/cluster/cluster.go index fd8a56290a..4a3fa0e71a 100644 --- a/tools/cluster/cluster.go +++ b/tools/cluster/cluster.go @@ -186,16 +186,6 @@ func (clu *Cluster) DeployChain(description string, allPeers, committeeNodes []i // // Register all non-committee nodes as access nodes. for _, a := range allPeers { - inCommittee := false - for _, c := range committeeNodes { - if c == a { - inCommittee = true - break - } - } - if inCommittee { - continue - } if err := clu.AddAccessNode(a, chain); err != nil { return nil, err } @@ -634,7 +624,7 @@ func dumpBalancesByColor(actual, expect colored.Balances) (string, bool) { assertionOk = false isOk = "FAIL" } - ret += fmt.Sprintf(" %s: %d (%d) %s\n", col, act, expect[col], isOk) + ret += fmt.Sprintf(" %s: %d (%d) %s\n", col.String(), act, expect[col], isOk) } lst = lst[:0] for col := range actual { diff --git a/tools/cluster/tests/account_test.go b/tools/cluster/tests/account_test.go index 32e4e2e8c2..4f0c2bd3e4 100644 --- a/tools/cluster/tests/account_test.go +++ b/tools/cluster/tests/account_test.go @@ -54,6 +54,7 @@ func TestBasicAccountsNLow(t *testing.T) { } func (e *chainEnv) testBasicAccounts(counter *cluster.MessageCounter) { + chainNodeCount := uint64(len(e.chain.AllPeers)) hname := iscp.Hn(incCounterSCName) description := "testing contract deployment with inccounter" programHash1 := inccounter.Contract.ProgramHash @@ -92,8 +93,8 @@ func (e *chainEnv) testBasicAccounts(counter *cluster.MessageCounter) { require.EqualValues(e.t, 42, counterValue) } - if !e.clu.VerifyAddressBalances(e.chain.ChainID.AsAddress(), ledgerstate.DustThresholdAliasOutputIOTA+2, - colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+2), "chain after deployment") { + if !e.clu.VerifyAddressBalances(e.chain.ChainID.AsAddress(), ledgerstate.DustThresholdAliasOutputIOTA+2+chainNodeCount, + colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+2+chainNodeCount), "chain after deployment") { e.t.Fail() } @@ -119,8 +120,8 @@ func (e *chainEnv) testBasicAccounts(counter *cluster.MessageCounter) { e.t.Fail() } - if !e.clu.VerifyAddressBalances(e.chain.ChainID.AsAddress(), ledgerstate.DustThresholdAliasOutputIOTA+transferIotas+2, - colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+transferIotas+2), "chain after") { + if !e.clu.VerifyAddressBalances(e.chain.ChainID.AsAddress(), ledgerstate.DustThresholdAliasOutputIOTA+transferIotas+2+chainNodeCount, + colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+transferIotas+2+chainNodeCount), "chain after") { e.t.Fail() } agentID := iscp.NewAgentID(e.chain.ChainID.AsAddress(), hname) @@ -154,6 +155,7 @@ func TestBasic2Accounts(t *testing.T) { root.ParamName: incCounterSCName, }) require.NoError(t, err) + chainNodeCount := uint64(len(chain.AllPeers)) if !counter.WaitUntilExpectationsMet() { t.Fail() @@ -181,16 +183,16 @@ func TestBasic2Accounts(t *testing.T) { require.EqualValues(t, 42, counterValue) } - if !e.clu.VerifyAddressBalances(chain.ChainID.AsAddress(), ledgerstate.DustThresholdAliasOutputIOTA+2, - colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+2), "chain after deployment") { + if !e.clu.VerifyAddressBalances(chain.ChainID.AsAddress(), ledgerstate.DustThresholdAliasOutputIOTA+2+chainNodeCount, + colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+2+chainNodeCount), "chain after deployment") { t.Fail() } originatorSigScheme := chain.OriginatorKeyPair() originatorAddress := chain.OriginatorAddress() - if !e.clu.VerifyAddressBalances(originatorAddress, solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-2, - colored.NewBalancesForIotas(solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-2), + if !e.clu.VerifyAddressBalances(originatorAddress, solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-2-chainNodeCount, + colored.NewBalancesForIotas(solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-2-chainNodeCount), "originator after deployment") { t.Fail() } @@ -217,16 +219,16 @@ func TestBasic2Accounts(t *testing.T) { require.NoError(t, err) require.EqualValues(t, 43, counterValue) } - if !e.clu.VerifyAddressBalances(originatorAddress, solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-2, - colored.NewBalancesForIotas(solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-2), + if !e.clu.VerifyAddressBalances(originatorAddress, solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-2-chainNodeCount, + colored.NewBalancesForIotas(solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-2-chainNodeCount), "originator after") { t.Fail() } if !e.clu.VerifyAddressBalances(myWalletAddr, solo.Saldo-transferIotas, colored.NewBalancesForIotas(solo.Saldo-transferIotas), "myWalletAddr after") { t.Fail() } - if !e.clu.VerifyAddressBalances(chain.ChainID.AsAddress(), ledgerstate.DustThresholdAliasOutputIOTA+2+transferIotas, - colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+2+transferIotas), + if !e.clu.VerifyAddressBalances(chain.ChainID.AsAddress(), ledgerstate.DustThresholdAliasOutputIOTA+2+transferIotas+chainNodeCount, + colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+2+transferIotas+chainNodeCount), "chain after") { t.Fail() } @@ -255,8 +257,8 @@ func TestBasic2Accounts(t *testing.T) { actual = chEnv.getBalanceOnChain(agentID, colored.IOTA) require.EqualValues(t, 0, actual) - if !e.clu.VerifyAddressBalances(originatorAddress, solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-3, - colored.NewBalancesForIotas(solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-3), + if !e.clu.VerifyAddressBalances(originatorAddress, solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-3-chainNodeCount, + colored.NewBalancesForIotas(solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-3-chainNodeCount), "originator after withdraw: "+originatorAddress.String()) { t.Fail() } diff --git a/tools/cluster/tests/advanced_inccounter_test.go b/tools/cluster/tests/advanced_inccounter_test.go index c11ee7f96c..769bc65f35 100644 --- a/tools/cluster/tests/advanced_inccounter_test.go +++ b/tools/cluster/tests/advanced_inccounter_test.go @@ -127,7 +127,7 @@ func testAccessNodesOnLedger(t *testing.T, numRequests, numValidatorNodes, clust e.printBlocks( numRequests + // The actual IncCounter requests. 3 + // Initial State + IncCounter SC Deploy + ??? - (clusterSize - numValidatorNodes), // Access node applications. + clusterSize, // Access node applications. ) } @@ -222,7 +222,7 @@ func testAccessNodesOffLedger(t *testing.T, numRequests, numValidatorNodes, clus e.printBlocks( numRequests + // The actual IncCounter requests. 4 + // ??? - (clusterSize - numValidatorNodes), // Access nodes applications. + clusterSize, // Access nodes applications. ) } @@ -259,7 +259,7 @@ func TestAccessNodesMany(t *testing.T) { e.printBlocks( posted + // The actual SC requests. 3 + // ??? - (clusterSize - numValidatorNodes), // GOV: Access Node Applications. + clusterSize, // GOV: Access Node Applications. ) } @@ -282,11 +282,6 @@ func TestRotation(t *testing.T) { chain, err := clu.DeployChain("chain", clu.Config.AllNodes(), cmt1, 3, addr1) require.NoError(t, err) t.Logf("chainID: %s", chain.ChainID.Base58()) - for _, i := range cmt1 { - // Add the first committee as access nodes as well, - // because they will be such after the committee rotation. - require.NoError(t, clu.AddAccessNode(i, chain)) - } description := "inccounter testing contract" programHash := inccounter.Contract.ProgramHash @@ -393,11 +388,6 @@ func TestRotationMany(t *testing.T) { chain, err := clu.DeployChain("chain", clu.Config.AllNodes(), cmt[0], quorum[0], addrs[0]) require.NoError(t, err) t.Logf("chainID: %s", chain.ChainID.Base58()) - for _, i := range cmt[0] { - // Add the first committee as access nodes as well, - // because they will be such after the committee rotation. - require.NoError(t, clu.AddAccessNode(i, chain)) - } govClient := chain.SCClient(governance.Contract.Hname(), chain.OriginatorKeyPair()) diff --git a/tools/cluster/tests/transfer_test.go b/tools/cluster/tests/transfer_test.go index b696da0241..15ccc66374 100644 --- a/tools/cluster/tests/transfer_test.go +++ b/tools/cluster/tests/transfer_test.go @@ -18,6 +18,7 @@ func TestDepositWithdraw(t *testing.T) { chain, err := e.clu.DeployDefaultChain() require.NoError(t, err) + chainNodeCount := uint64(len(chain.AllPeers)) chEnv := newChainEnv(t, e.clu, chain) @@ -30,13 +31,13 @@ func TestDepositWithdraw(t *testing.T) { "myAddress begin") { t.Fail() } - if !e.clu.VerifyAddressBalances(chain.OriginatorAddress(), solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-1, - colored.NewBalancesForIotas(solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-1), + if !e.clu.VerifyAddressBalances(chain.OriginatorAddress(), solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-1-chainNodeCount, + colored.NewBalancesForIotas(solo.Saldo-ledgerstate.DustThresholdAliasOutputIOTA-1-chainNodeCount), "originatorAddress begin") { t.Fail() } - if !e.clu.VerifyAddressBalances(chain.ChainAddress(), ledgerstate.DustThresholdAliasOutputIOTA+1, - colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+1), + if !e.clu.VerifyAddressBalances(chain.ChainAddress(), ledgerstate.DustThresholdAliasOutputIOTA+1+chainNodeCount, + colored.NewBalancesForIotas(ledgerstate.DustThresholdAliasOutputIOTA+1+chainNodeCount), "chainAddress begin") { t.Fail() } diff --git a/tools/cluster/tests/util.go b/tools/cluster/tests/util.go index 4a6fa8bd9f..76a2368139 100644 --- a/tools/cluster/tests/util.go +++ b/tools/cluster/tests/util.go @@ -157,7 +157,7 @@ func (e *chainEnv) printAccounts(title string) { for aid, bals := range allBalances { s += fmt.Sprintf(" %s\n", aid.String()) for k, v := range bals { - s += fmt.Sprintf(" %s: %d\n", k, v) + s += fmt.Sprintf(" %s: %d\n", k.String(), v) } } fmt.Println(s) From 7c280ce9269b55982ba0f627163c8b8da4674901 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Mon, 10 Jan 2022 19:36:23 +0200 Subject: [PATCH 086/120] Cleanup. --- packages/chain/statemgr/eventproc.go | 1 + packages/chain/statemgr/syncing_block.go | 25 +++++++++++++++++++ packages/chain/statemgr/syncmgr.go | 8 ++---- .../cluster/tests/advanced_inccounter_test.go | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/packages/chain/statemgr/eventproc.go b/packages/chain/statemgr/eventproc.go index a1b6d21f4c..cba11f8ab1 100644 --- a/packages/chain/statemgr/eventproc.go +++ b/packages/chain/statemgr/eventproc.go @@ -55,6 +55,7 @@ func (sm *stateManager) EnqueueBlockMsg(msg *messages.BlockMsgIn) { } func (sm *stateManager) handleBlockMsg(msg *messages.BlockMsgIn) { + sm.syncingBlocks.blockReceived() sm.log.Debugw("handleBlockMsg: ", "sender", msg.SenderPubKey.String(), ) diff --git a/packages/chain/statemgr/syncing_block.go b/packages/chain/statemgr/syncing_block.go index 2760024a01..08db76b5c9 100644 --- a/packages/chain/statemgr/syncing_block.go +++ b/packages/chain/statemgr/syncing_block.go @@ -13,10 +13,16 @@ import ( "github.com/iotaledger/wasp/packages/state" ) +const ( + pollFallbackDelay = 5 * time.Second +) + type syncingBlocks struct { blocks map[uint32]*syncingBlock // StateIndex -> BlockCandidates log *logger.Logger initialBlockRetry time.Duration + lastPullTime time.Time // Time, when we pulled for some block last time. + lastRecvTime time.Time // Time, when we received any block we pulled. Used to determine, if fallback nodes should be used. } type syncingBlock struct { @@ -183,3 +189,22 @@ func (syncsT *syncingBlocks) restartSyncing() { func (syncsT *syncingBlocks) deleteSyncingBlock(stateIndex uint32) { delete(syncsT.blocks, stateIndex) } + +// +// Track poll/reception times, to determine, if no one responds to our polls. +// + +func (syncsT *syncingBlocks) blocksPulled() { + syncsT.lastPullTime = time.Now() +} + +func (syncsT *syncingBlocks) blockReceived() { + syncsT.lastRecvTime = time.Now() +} + +func (syncsT *syncingBlocks) blockPollFallbackNeeded() bool { + if len(syncsT.blocks) == 0 { + return false + } + return syncsT.lastPullTime.Sub(syncsT.lastRecvTime) >= pollFallbackDelay +} diff --git a/packages/chain/statemgr/syncmgr.go b/packages/chain/statemgr/syncmgr.go index 18e911ca25..ec45241a2c 100644 --- a/packages/chain/statemgr/syncmgr.go +++ b/packages/chain/statemgr/syncmgr.go @@ -75,12 +75,7 @@ func (sm *stateManager) doSyncActionIfNeeded() { // not synced startSyncFromIndex := sm.solidState.BlockIndex() + 1 sm.log.Debugf("doSyncAction: trying to sync state from index %v to %v", startSyncFromIndex, sm.stateOutput.GetStateIndex()) - if !sm.domain.HaveMainPeers() { - // - // - // TODO: XXX: KP: Other conditions... - // - // + if !sm.domain.HaveMainPeers() || sm.syncingBlocks.blockPollFallbackNeeded() { sm.domain.SetFallbackMode(true) } for i := startSyncFromIndex; i <= sm.stateOutput.GetStateIndex(); i++ { @@ -101,6 +96,7 @@ func (sm *stateManager) doSyncActionIfNeeded() { getBlockMsg := &messages.GetBlockMsg{BlockIndex: i} for _, p := range sm.domain.GetRandomOtherPeers(numberOfNodesToRequestBlockFromConst) { sm.domain.SendMsgByPubKey(p, peering.PeerMessageReceiverStateManager, peerMsgTypeGetBlock, util.MustBytes(getBlockMsg)) + sm.syncingBlocks.blocksPulled() sm.log.Debugf("doSyncAction: requesting block index %v,from %v", i, p.String()) } sm.syncingBlocks.startSyncingIfNeeded(i) diff --git a/tools/cluster/tests/advanced_inccounter_test.go b/tools/cluster/tests/advanced_inccounter_test.go index 769bc65f35..f2fd6b16b3 100644 --- a/tools/cluster/tests/advanced_inccounter_test.go +++ b/tools/cluster/tests/advanced_inccounter_test.go @@ -40,7 +40,7 @@ func setupAdvancedInccounterTest(t *testing.T, clusterSize int, committee []int) t.Logf("generated state address: %s", addr.Base58()) - chain, err := clu.DeployChain("chain", clu.Config.AllNodes(), committee, quorum, addr) // TODO: XXX: KP: AllNodes is used? + chain, err := clu.DeployChain("chain", clu.Config.AllNodes(), committee, quorum, addr) require.NoError(t, err) t.Logf("deployed chainID: %s", chain.ChainID.Base58()) From 09a36bf361f7ea65fd20fc6d7a1b6b531109a7f8 Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Mon, 10 Jan 2022 20:25:02 +0200 Subject: [PATCH 087/120] Consensus diagram updated to reflect the current situation --- .../chain/consensus/consensus_diagram.png | Bin 645023 -> 694360 bytes .../chain/consensus/consensus_diagram.uxf | 630 +++++++++++------- 2 files changed, 401 insertions(+), 229 deletions(-) diff --git a/packages/chain/consensus/consensus_diagram.png b/packages/chain/consensus/consensus_diagram.png index 5f4c2711980ad76deb2a470069a387c441762d56..280348ff62bfac5037524d45a75c001d88a967c8 100644 GIT binary patch literal 694360 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

K0 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 ~SesglPO<6fv;z$oL?AyD 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®3HcX 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

    @y&4Yy^q{?+~A-!xJFcd{Hg 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

    TP)YRu8q@!R%Mmn)2BoI! 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

    2lN)s38i-C3K^%!!|WyUgW;{hQl?E>l)r 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

    K$4DtlY%HDq@!m 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 y<1b0U;}DsA`Jsx$Jhd~ diff --git a/packages/chain/consensus/consensus_diagram.uxf b/packages/chain/consensus/consensus_diagram.uxf index ed462028a2..aa62251c29 100644 --- a/packages/chain/consensus/consensus_diagram.uxf +++ b/packages/chain/consensus/consensus_diagram.uxf @@ -6,12 +6,12 @@ 603 36 - 441 + 432 513 lt=.> r2=/ACSConsensus(proposal, acsSessionID, stateOutput.Index())/ - 10.0;550.0;110.0;550.0;110.0;20.0;470.0;20.0 + 10.0;550.0;110.0;550.0;110.0;20.0;460.0;20.0 UMLSpecialState @@ -27,25 +27,25 @@ r2=/ACSConsensus(proposal, acsSessionID, stateOutput.Index())/ Relation - 36 + 72 108 - 261 + 225 54 lt=-> -m1=!stateReceived || workflowFinished - 270.0;30.0;50.0;30.0;50.0;10.0 +m1=!stateReceived || !inProgress + 230.0;30.0;10.0;30.0;10.0;10.0 Relation 288 144 - 252 + 207 54 lt=-> -m2=stateReceived && !workflowFinished +m2=stateReceived && inProgress 10.0;10.0;10.0;40.0 @@ -53,8 +53,8 @@ m2=stateReceived && !workflowFinished 0 0 - 1719 - 2403 + 1710 + 2439 _Wasp:this_ @@ -77,13 +77,13 @@ valign=top UMLClass - 1773 + 1764 0 90 36 *Consensus* -version: 3 +version: 4 @@ -239,7 +239,7 @@ halign=left UMLDeployment - 1026 + 1017 27 99 36 @@ -250,7 +250,7 @@ halign=left Relation - 1062 + 1053 54 342 54 @@ -441,14 +441,15 @@ len(reqs) == 0 UMLState - 585 - 2124 + 657 + 2142 270 - 261 + 279 *resetWorkflow()* -- resultSignatures[i] := nil for all i +resultSigAck := [] acsSessionID++ resultState := nil resultTxEssence := nil @@ -459,11 +460,11 @@ stateReceived := (stateOutput != nil) batchProposalSent := false consensusBatchKnown := false vmStarted := false -vmResultSignedAndBroadcasted := false +vmResultSigned := false transactionFinalized := false transactionPosted := false transactionSeen := false -workflowFinished := false +inProgress := (stateOutput != nil) valign=top @@ -509,17 +510,17 @@ halign=left 450 837 - 594 + 585 234 lt=.> r1=/Run(task)/ - 10.0;240.0;450.0;240.0;450.0;10.0;640.0;10.0 + 10.0;240.0;450.0;240.0;450.0;10.0;630.0;10.0 UMLDeployment - 1026 + 1017 828 117 36 @@ -530,7 +531,7 @@ r1=/Run(task)/ Relation - 1071 + 1062 855 144 54 @@ -555,7 +556,7 @@ m2=/VMResultMsg(task)/ UMLState 54 - 1278 + 1296 801 567 @@ -569,7 +570,7 @@ valign=top Relation 81 - 1323 + 1341 45 27 @@ -580,7 +581,7 @@ valign=top UMLSpecialState 72 - 1323 + 1341 18 18 @@ -591,7 +592,7 @@ valign=top UMLSpecialState 108 - 1314 + 1332 36 36 @@ -602,7 +603,7 @@ valign=top Relation 135 - 1314 + 1332 774 603 @@ -614,7 +615,7 @@ r1=transactionFinalized || !vmResultSigned Relation 117 - 1341 + 1359 288 54 @@ -626,7 +627,7 @@ m2=!transactionFinalized && vmResultSigned UMLState 72 - 1377 + 1395 675 36 @@ -637,8 +638,8 @@ halign=left Relation - 117 - 1404 + 99 + 1422 27 45 @@ -649,8 +650,8 @@ halign=left UMLSpecialState - 108 - 1431 + 90 + 1449 36 36 @@ -660,8 +661,8 @@ halign=left Relation - 117 - 1458 + 99 + 1476 288 54 @@ -672,20 +673,20 @@ len(contributors) >= committee.Quorum() Relation - 135 - 1431 - 765 + 117 + 1449 + 783 477 lt=-> r1=len(contributors) < committee.Quorum() - 10.0;20.0;830.0;20.0;830.0;510.0;760.0;510.0 + 10.0;20.0;850.0;20.0;850.0;510.0;780.0;510.0 UMLSpecialState - 108 - 1494 + 90 + 1512 36 36 @@ -695,20 +696,20 @@ r1=len(contributors) < committee.Quorum() Relation - 135 - 1494 - 594 + 117 + 1512 + 612 36 lt=-> for some i DKShare.VerifySig(resultTxEssence, resultSignatures[contributors[i]]) is error - 10.0;20.0;640.0;20.0 + 10.0;20.0;660.0;20.0 Relation - 117 - 1521 + 99 + 1539 549 54 @@ -732,7 +733,7 @@ m2=for all i DKShare.VerifySig(resultTxEssence, resultSignatures[contributors[i] Relation 810 - 1503 + 1521 81 396 @@ -744,7 +745,7 @@ m2=for all i DKShare.VerifySig(resultTxEssence, resultSignatures[contributors[i] UMLState 711 - 1503 + 1521 126 27 @@ -756,7 +757,7 @@ halign=left UMLState 243 - 1800 + 1818 594 27 @@ -767,8 +768,8 @@ halign=left UMLDeployment - 1026 - 1728 + 1017 + 1746 99 36 @@ -778,8 +779,8 @@ halign=left Relation - 1062 - 1755 + 1053 + 1773 360 54 @@ -791,19 +792,19 @@ m2=/StateTransitionMsg(state, output, outputTimestamp)/ Relation 504 - 1728 - 540 + 1746 + 531 36 lt=.> /StateCandidateMsg(resultState, chainOutput.ID())/ - 10.0;20.0;580.0;20.0 + 10.0;20.0;570.0;20.0 Relation 99 - 1701 + 1719 27 45 @@ -815,7 +816,7 @@ m2=/StateTransitionMsg(state, output, outputTimestamp)/ UMLSpecialState 90 - 1791 + 1809 36 36 @@ -826,7 +827,7 @@ m2=/StateTransitionMsg(state, output, outputTimestamp)/ Relation 99 - 1818 + 1836 126 63 @@ -839,7 +840,7 @@ m2pos=0,-10 Relation 117 - 1791 + 1809 144 36 @@ -850,8 +851,8 @@ iAmContributor UMLState - 81 - 1557 + 72 + 1575 594 153 @@ -871,7 +872,7 @@ halign=left Relation 252 - 1818 + 1836 27 63 @@ -883,7 +884,7 @@ halign=left UMLSpecialState 72 - 1908 + 1926 18 18 @@ -894,7 +895,7 @@ halign=left UMLSpecialState 108 - 1899 + 1917 36 36 @@ -905,7 +906,7 @@ halign=left Relation 81 - 1908 + 1926 45 27 @@ -916,7 +917,7 @@ halign=left Relation 117 - 1926 + 1944 414 63 @@ -929,7 +930,7 @@ transactionSeen || Now is before postTxDeadline Relation 135 - 1899 + 1917 468 45 @@ -942,7 +943,7 @@ transactionFinalized && iAmContributor && !transactionPosted &am UMLState 585 - 1899 + 1917 189 36 @@ -954,19 +955,19 @@ halign=left Relation 765 - 1899 - 1053 + 1917 + 1044 153 lt=.> /MsgPostTransaction(finalTx)/ - 10.0;20.0;1150.0;20.0;1150.0;150.0 + 10.0;20.0;1140.0;20.0;1140.0;150.0 Relation 630 - 1926 + 1944 27 63 @@ -978,7 +979,7 @@ halign=left UMLState 54 - 1971 + 1989 873 117 @@ -992,7 +993,7 @@ valign=top UMLSpecialState 72 - 2007 + 2025 18 18 @@ -1003,7 +1004,7 @@ valign=top Relation 81 - 2007 + 2025 270 45 @@ -1014,7 +1015,7 @@ valign=top UMLSpecialState 315 - 2034 + 2052 36 36 @@ -1027,18 +1028,18 @@ valign=top 27 99 306 - 1980 + 1998 lt=-> !transactionFinalized || transactionSeen || Now is before pullInclusionStateDeadline - 320.0;2170.0;10.0;2170.0;320.0;2170.0;10.0;2170.0;10.0;10.0;30.0;10.0 + 320.0;2190.0;10.0;2190.0;320.0;2190.0;10.0;2190.0;10.0;10.0;30.0;10.0 Relation 342 - 2034 + 2052 324 45 @@ -1051,7 +1052,7 @@ Now is not before pullInclusionStateDeadline UMLState 648 - 2034 + 2052 261 36 @@ -1065,29 +1066,29 @@ halign=left 18 81 774 - 2043 + 2061 lt=-> - 840.0;2210.0;840.0;2250.0;10.0;2250.0;10.0;10.0;40.0;10.0 + 840.0;2230.0;840.0;2270.0;10.0;2270.0;10.0;10.0;40.0;10.0 Relation 900 - 2034 - 855 + 2052 + 846 36 lt=.> /MsgGetTxInclusionState(chain.ID(), finalTx.ID())/ - 10.0;20.0;930.0;20.0 + 10.0;20.0;920.0;20.0 UMLDeployment - 1737 - 2034 + 1728 + 2052 126 36 @@ -1097,8 +1098,8 @@ halign=left Relation - 1395 - 2061 + 1386 + 2079 423 54 @@ -1146,7 +1147,7 @@ TimerTick UMLSpecialState - 1062 + 1053 891 36 36 @@ -1157,7 +1158,7 @@ TimerTick Relation - 1089 + 1080 891 360 45 @@ -1170,7 +1171,7 @@ acsSessionID != task.ACSSessionID UMLSpecialState - 1431 + 1422 900 18 18 @@ -1181,7 +1182,7 @@ acsSessionID != task.ACSSessionID Relation - 1071 + 1062 918 486 54 @@ -1193,13 +1194,14 @@ acsSessionID != task.ACSSessionID UMLState - 1026 + 1017 1071 - 405 - 99 + 441 + 117 essenceHash = Hash(resultTxEssence) sigShare = DKShare.Sign(resultTxEssence) +resultSignatures[ownPeerIndex].ChainInputID = task.ChainInput.ID() resultSignatures[ownPeerIndex].SenderIndex = ownPeerIndex resultSignatures[ownPeerIndex].EssenceHash = essenceHash resultSignatures[ownPeerIndex].SigShare = sigShare @@ -1211,30 +1213,30 @@ halign=left Relation 324 - 1206 - 1431 + 1224 + 1422 36 lt=.> r1=/SignedResultMsg(ownPeerIndex, stateOutput.ID(), essenceHash, sigShare, Now)/ - 10.0;20.0;1570.0;20.0 + 10.0;20.0;1560.0;20.0 Relation 306 126 - 738 + 729 1044 lt=-> - 800.0;1140.0;770.0;1140.0;770.0;10.0;10.0;10.0 + 790.0;1140.0;760.0;1140.0;760.0;10.0;10.0;10.0 UMLState - 1026 - 2115 + 1017 + 2133 153 27 @@ -1245,8 +1247,8 @@ halign=left UMLSpecialState - 1368 - 2079 + 1359 + 2097 36 36 @@ -1256,8 +1258,8 @@ halign=left Relation - 1377 - 2106 + 1368 + 2124 288 54 @@ -1268,8 +1270,8 @@ halign=left UMLSpecialState - 1377 - 2142 + 1368 + 2160 18 18 @@ -1279,8 +1281,8 @@ halign=left Relation - 1089 - 2079 + 1080 + 2097 297 54 @@ -1291,8 +1293,8 @@ transactionFinalized && txID == finalTx.ID() Relation - 1089 - 2133 + 1080 + 2151 27 45 @@ -1303,8 +1305,8 @@ transactionFinalized && txID == finalTx.ID() UMLSpecialState - 1080 - 2160 + 1071 + 2178 36 36 @@ -1314,8 +1316,8 @@ transactionFinalized && txID == finalTx.ID() Relation - 1089 - 2187 + 1080 + 2205 126 54 @@ -1326,8 +1328,8 @@ m2=state is Rejected UMLState - 1026 - 2223 + 1017 + 2241 126 27 @@ -1340,18 +1342,18 @@ halign=left 306 126 - 738 - 2133 + 729 + 2151 lt=-> - 800.0;2350.0;720.0;2350.0;720.0;10.0;10.0;10.0 + 790.0;2370.0;720.0;2370.0;720.0;10.0;10.0;10.0 Relation - 1107 - 2160 + 1098 + 2178 171 36 @@ -1362,12 +1364,12 @@ state is Confirmed UMLState - 1260 - 2169 + 1251 + 2187 180 45 - finished = true + inProgress = false *refreshConsensusInfo()* halign=left @@ -1377,18 +1379,18 @@ halign=left 306 126 - 1062 - 2160 + 1053 + 2178 lt=-> - 1160.0;2320.0;1160.0;2380.0;710.0;2380.0;710.0;10.0;10.0;10.0 + 1150.0;2340.0;1150.0;2400.0;710.0;2400.0;710.0;10.0;10.0;10.0 UMLState - 873 - 2286 + 945 + 2322 351 99 @@ -1406,7 +1408,7 @@ valign=top UMLSpecialState - 1053 + 1044 90 36 36 @@ -1417,7 +1419,7 @@ valign=top Relation - 1080 + 1071 90 387 36 @@ -1429,7 +1431,7 @@ acsSessionID != sessionID || consensusBatchKnown UMLSpecialState - 1449 + 1440 99 18 18 @@ -1440,7 +1442,7 @@ acsSessionID != sessionID || consensusBatchKnown Relation - 1062 + 1053 117 369 54 @@ -1452,7 +1454,7 @@ acsSessionID == sessionID && !consensusBatchKnown UMLSpecialState - 1053 + 1044 153 36 36 @@ -1463,7 +1465,7 @@ acsSessionID == sessionID && !consensusBatchKnown Relation - 1080 + 1071 153 432 72 @@ -1478,7 +1480,7 @@ for some i, j: acs[i].ValidatorIndex == acs[j].ValidatorIndex UMLState - 1494 + 1485 162 126 27 @@ -1490,7 +1492,7 @@ halign=left Relation - 1062 + 1053 180 369 135 @@ -1505,7 +1507,7 @@ for all i, j: acs[i].ValidatorIndex != acs[j].ValidatorIndex UMLState - 1026 + 1017 297 495 63 @@ -1519,7 +1521,7 @@ halign=left UMLState - 1206 + 1197 531 270 45 @@ -1532,7 +1534,7 @@ halign=left Relation - 1062 + 1053 351 27 45 @@ -1543,7 +1545,7 @@ halign=left UMLSpecialState - 1053 + 1044 378 36 36 @@ -1554,7 +1556,7 @@ halign=left Relation - 1080 + 1071 378 189 36 @@ -1566,7 +1568,7 @@ len(inBatchSet) == 0 UMLSpecialState - 1539 + 1530 387 18 18 @@ -1577,7 +1579,7 @@ len(inBatchSet) == 0 UMLState - 1251 + 1242 378 270 36 @@ -1590,7 +1592,7 @@ halign=left Relation - 1512 + 1503 387 45 27 @@ -1601,7 +1603,7 @@ halign=left Relation - 1062 + 1053 405 144 54 @@ -1613,7 +1615,7 @@ m2=len(inBatchSet) > 0 UMLState - 1026 + 1017 441 594 72 @@ -1628,7 +1630,7 @@ halign=left UMLSpecialState - 1053 + 1044 531 36 36 @@ -1639,7 +1641,7 @@ halign=left Relation - 1062 + 1053 504 27 45 @@ -1650,7 +1652,7 @@ halign=left Relation - 1080 + 1071 531 144 36 @@ -1662,7 +1664,7 @@ DKShare error UMLState - 1026 + 1017 594 594 207 @@ -1686,7 +1688,7 @@ halign=left Relation - 1062 + 1053 558 99 54 @@ -1698,7 +1700,7 @@ m2=DKShare ok Relation - 1467 + 1458 558 45 54 @@ -1711,11 +1713,11 @@ m2=DKShare ok 306 126 - 738 + 729 675 lt=-> - 800.0;730.0;780.0;730.0;780.0;10.0;10.0;10.0 + 790.0;730.0;770.0;730.0;770.0;10.0;10.0;10.0 UMLSpecialState @@ -1765,8 +1767,8 @@ halign=center UMLState - 1026 - 1791 + 1017 + 1809 405 81 @@ -1783,19 +1785,19 @@ halign=left 306 126 - 738 - 1746 + 729 + 1764 lt=-> - 800.0;1920.0;740.0;1920.0;740.0;10.0;10.0;10.0 + 790.0;1940.0;740.0;1940.0;740.0;10.0;10.0;10.0 UMLDeployment - 1737 - 1206 + 1728 + 1224 126 - 36 + 342 _Wasp:Other_ @@ -1803,20 +1805,21 @@ halign=left Relation - 1359 - 1233 - 513 - 54 + 1341 + 1269 + 405 + 45 lt=.> -r2=/SignedResultMsg(senderIndex, chainInputID, essenceHash, sigShare, Now)/ - 480.0;10.0;480.0;40.0;10.0;40.0 +/SignedResultMsg(senderIndex, chainInputID, essenceHash,/ +/sigShare, Now)/ + 430.0;20.0;10.0;20.0 UMLSpecialState - 1332 - 1251 + 1314 + 1269 36 36 @@ -1826,46 +1829,41 @@ r2=/SignedResultMsg(senderIndex, chainInputID, essenceHash, sigShare, Now)/ Relation - 306 - 126 - 1044 - 1224 + 1062 + 1269 + 270 + 54 lt=-> stateOutput == nil || stateOutput.ID() != chainInputID || -resultSignatures[senderIndex] != nil || -sigShare >= committee.Size() || -sigShare == ownPeerIndex || -sigShare != senderIndex - 1140.0;1270.0;760.0;1270.0;1140.0;1270.0;760.0;1270.0;760.0;10.0;10.0;10.0 +resultSignatures[senderIndex] != nil + 280.0;20.0;10.0;20.0 Relation - 1341 - 1278 - 279 - 117 + 1323 + 1296 + 261 + 81 lt=-> stateOutput != nil && stateOutput.ID() == chainInputID && -resultSignatures[senderIndex] == nil && -sigShare < committee.Size() && -sigShare != ownPeerIndex && -sigShare == senderIndex - 10.0;10.0;10.0;80.0;10.0;10.0;10.0;110.0 +resultSignatures[senderIndex] == nil + 10.0;10.0;10.0;50.0;10.0;10.0;10.0;70.0 UMLState - 1026 - 1377 + 1017 + 1413 387 - 54 + 72 - resultSignatures[senderIndex].SenderIndex = senderIndex + resultSignatures[senderIndex].ChainInputID = chainInputID +resultSignatures[senderIndex].SenderIndex = senderIndex resultSignatures[senderIndex].EssenceHash = essenceHash resultSignatures[senderIndex].SigShare = sigShare halign=left @@ -1876,18 +1874,18 @@ halign=left 306 126 - 738 - 1305 + 729 + 1431 lt=-> - 800.0;1430.0;750.0;1430.0;750.0;10.0;10.0;10.0 + 790.0;1570.0;750.0;1570.0;750.0;10.0;10.0;10.0 UMLState 54 - 1863 + 1881 765 90 @@ -1900,7 +1898,7 @@ valign=top Relation - 1458 + 1449 99 117 81 @@ -1913,12 +1911,12 @@ valign=top 306 126 - 792 - 2070 + 783 + 2088 lt=-> r1= state is Pending - 860.0;2280.0;730.0;2280.0;730.0;10.0;10.0;10.0 + 850.0;2300.0;730.0;2300.0;730.0;10.0;10.0;10.0 UMLState @@ -1926,7 +1924,7 @@ r1= state is Pending 54 1107 297 - 153 + 171 *broadcastSignedResultIfNeeded()* -- @@ -1972,30 +1970,32 @@ valign=top 117 1170 - 405 - 54 + 423 + 72 lt=-> -vmResultSigned && Now is after delaySendingSignedResult - 10.0;10.0;10.0;20.0;10.0;10.0;10.0;40.0 +vmResultSigned && len(resultSigAck) < committee.Size()-1 && +Now is after delaySendingSignedResult + 10.0;10.0;10.0;40.0;10.0;10.0;10.0;60.0 Relation 135 1143 - 423 - 153 + 450 + 171 lt=-> -!vmResultSigned || Now is not after delaySendingSignedResult - 10.0;20.0;450.0;20.0;450.0;150.0 +!vmResultSigned || len(resultSigAck) >= committee.Size()-1 || +Now is not after delaySendingSignedResult + 10.0;20.0;480.0;20.0;480.0;170.0 UMLState 72 - 1206 + 1224 261 36 @@ -2008,7 +2008,7 @@ halign=left Relation 117 - 1233 + 1251 27 63 @@ -2020,7 +2020,7 @@ halign=left UMLSpecialState 90 - 1728 + 1746 36 36 @@ -2031,7 +2031,7 @@ halign=left Relation 117 - 1728 + 1746 279 36 @@ -2043,7 +2043,7 @@ halign=left Relation 99 - 1755 + 1773 243 54 @@ -2055,7 +2055,7 @@ m2=chainOutput.isGovernanceUpdated UMLState 378 - 1737 + 1755 135 27 @@ -2066,7 +2066,7 @@ halign=left UMLSpecialState - 1062 + 1053 954 36 36 @@ -2077,7 +2077,7 @@ halign=left Relation - 1089 + 1080 954 288 36 @@ -2089,7 +2089,7 @@ result.RotationAddress != nil Relation - 1071 + 1062 981 207 54 @@ -2101,7 +2101,7 @@ result.RotationAddress == nil UMLState - 1026 + 1017 1017 324 36 @@ -2114,7 +2114,7 @@ halign=left UMLState - 1359 + 1350 954 333 99 @@ -2131,7 +2131,7 @@ halign=left Relation - 1071 + 1062 1044 27 45 @@ -2142,7 +2142,7 @@ halign=left Relation - 1377 + 1368 1044 27 45 @@ -2150,4 +2150,176 @@ halign=left lt=-> 10.0;10.0;10.0;30.0 + + UMLSpecialState + + 1314 + 1359 + 36 + 36 + + type=decision + + + + Relation + + 1089 + 1359 + 243 + 72 + + lt=-> +sigShare < committee.Size() && +sigShare != ownPeerIndex && +sigShare == senderIndex + 250.0;20.0;10.0;20.0;10.0;60.0 + + + Relation + + 1152 + 1359 + 486 + 180 + + lt=-> +sigShare >= committee.Size() || +sigShare == ownPeerIndex || +sigShare != senderIndex + 220.0;20.0;300.0;20.0;300.0;40.0;300.0;20.0;300.0;160.0;10.0;160.0;10.0;180.0 + + + UMLState + + 1017 + 1521 + 162 + 27 + + sendSignedResultAck() +halign=left + + + + Relation + + 1089 + 1476 + 27 + 63 + + lt=-> + 10.0;10.0;10.0;50.0 + + + Relation + + 1170 + 1512 + 576 + 36 + + lt=.> +r1=/SignedResultAckMsg(ownPeerIndex, chainInputID, essenceHash)/ + 10.0;20.0;620.0;20.0 + + + Relation + + 1305 + 1539 + 441 + 54 + + lt=.> +r1=/SignedResultAckMsg(senderIndex, chainInputID, essenceHash)/ + 470.0;20.0;90.0;20.0;80.0;40.0 + + + UMLSpecialState + + 1359 + 1575 + 36 + 36 + + type=decision + + + + UMLSpecialState + + 1053 + 1278 + 18 + 18 + + type=final + + + + Relation + + 1368 + 1602 + 333 + 90 + + lt=-> +ownSignature := resultSignatures[ownPeerIndex] +ownSignature == nil || +ownSignature.EssenceHash != essenceHash || +ownSignature.ChainInputID != chainInputID + 10.0;10.0;10.0;50.0;10.0;10.0;10.0;80.0 + + + UMLSpecialState + + 1368 + 1674 + 18 + 18 + + type=final + + + + Relation + + 1026 + 1575 + 351 + 108 + + lt=-> +ownSignature := resultSignatures[ownPeerIndex] +ownSignature == nil || +ownSignature.EssenceHash != essenceHash || +ownSignature.ChainInputID != chainInputID + 370.0;20.0;10.0;20.0;10.0;100.0 + + + UMLState + + 1017 + 1665 + 234 + 27 + + append(resultSigAck, senderIndex) +halign=left + + + + Relation + + 1242 + 1674 + 144 + 27 + + lt=-> + 10.0;10.0;140.0;10.0 + From f885cfce0ce1ad0d19b07c2a1dd5db75a95c0a3f Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Mon, 10 Jan 2022 20:25:17 +0200 Subject: [PATCH 088/120] Consensus logging improved --- packages/chain/consensus/action.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/chain/consensus/action.go b/packages/chain/consensus/action.go index 7dd8db883b..0f7bd5e9ab 100644 --- a/packages/chain/consensus/action.go +++ b/packages/chain/consensus/action.go @@ -256,7 +256,10 @@ func (c *consensus) broadcastSignedResultIfNeeded() { c.log.Debugf("broadcastSignedResult not needed: vm result is not signed") return } - if len(c.resultSigAck) >= int(c.committee.Size()-1) { + acksReceived := len(c.resultSigAck) + acksNeeded := int(c.committee.Size() - 1) + if acksReceived >= acksNeeded { + c.log.Debugf("broadcastSignedResult not needed: acks received from %v peers, only %v needed", acksReceived, acksNeeded) return } if time.Now().After(c.delaySendingSignedResult) { @@ -564,7 +567,7 @@ func (c *consensus) receiveACS(values [][]byte, sessionID uint64) { func (c *consensus) processInclusionState(msg *messages.InclusionStateMsg) { if !c.workflow.transactionFinalized { - c.log.Debugf("processInclusionState: transaction finalized -> skipping.") + c.log.Debugf("processInclusionState: transaction not finalized -> skipping.") return } if msg.TxID != c.finalTx.ID() { @@ -770,15 +773,29 @@ func (c *consensus) receiveSignedResult(msg *messages.SignedResultMsgIn) { func (c *consensus) receiveSignedResultAck(msg *messages.SignedResultAckMsgIn) { own := c.resultSignatures[c.committee.OwnPeerIndex()] - if own == nil || msg.EssenceHash != own.EssenceHash || msg.ChainInputID != own.ChainInputID { + if own == nil { + c.log.Debugf("receiveSignedResultAck: ack from %v ignored, because own signature is nil", msg.SenderIndex) return } + if msg.EssenceHash != own.EssenceHash { + c.log.Debugf("receiveSignedResultAck: ack from %v ignored, because essence hash in ack %v is different than own signature essence hash %v", + msg.SenderIndex, msg.EssenceHash.String(), own.EssenceHash.String()) + return + } + if msg.ChainInputID != own.ChainInputID { + c.log.Debugf("receiveSignedResultAck: ack from %v ignored, because chain input id in ack %v is different than own chain input id %v", + msg.SenderIndex, iscp.OID(msg.ChainInputID), iscp.OID(own.ChainInputID)) + return + } + for _, i := range c.resultSigAck { if i == msg.SenderIndex { + c.log.Debugf("receiveSignedResultAck: ack from %v ignored, because it has already been received", msg.SenderIndex) return } } c.resultSigAck = append(c.resultSigAck, msg.SenderIndex) + c.log.Debugf("receiveSignedResultAck: ack from %v accepted; acks from nodes %v have already been received", msg.SenderIndex, c.resultSigAck) } // TODO mutex inside is not good From 947792dc06de8a8da0062d70b607813db1cc5741 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Mon, 10 Jan 2022 10:32:13 -0800 Subject: [PATCH 089/120] Removed unnecessary minus from call args --- packages/vm/wasmlib/ts/wasmclient/arguments.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index dcce6a43a6..519648a0b6 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -164,11 +164,10 @@ export class Arguments { return buf; } - // {"Items":[{"Key":"YQ==","Value":"AHDllHMJmeQf4m2jKzi+S+sLkLlpZuSJy5lpIvaJ8JN2AAAAAA=="}]} encodeCall(): wasmclient.Items { let items = new wasmclient.Items() for (const [key, val] of this.args) { - const k = Buffer.from("-" + key).toString("base64"); + const k = Buffer.from(key).toString("base64"); const v = val.toString("base64"); items.Items.push(new wasmclient.Item(k, v)) } From 259e793a98985a85ce718f7e19bea2fe93f5e80d Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Mon, 10 Jan 2022 21:08:52 +0200 Subject: [PATCH 090/120] Some heavy tests are running, if run one by one. --- tools/cluster/tests/advanced_inccounter_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cluster/tests/advanced_inccounter_test.go b/tools/cluster/tests/advanced_inccounter_test.go index f2fd6b16b3..d9fd967fc5 100644 --- a/tools/cluster/tests/advanced_inccounter_test.go +++ b/tools/cluster/tests/advanced_inccounter_test.go @@ -172,7 +172,7 @@ func TestAccessNodesOffLedger(t *testing.T) { t.Run("cluster=30,N=15,req=8", func(t *testing.T) { testutil.RunHeavy(t) - const waitFor = 60 * time.Second + const waitFor = 180 * time.Second const numRequests = 8 const numValidatorNodes = 15 const clusterSize = 30 @@ -181,7 +181,7 @@ func TestAccessNodesOffLedger(t *testing.T) { t.Run("cluster=30,N=20,req=8", func(t *testing.T) { testutil.RunHeavy(t) - const waitFor = 60 * time.Second + const waitFor = 300 * time.Second const numRequests = 8 const numValidatorNodes = 20 const clusterSize = 30 From d50c780fba53c3b7b108cd4a93d0d8a6071310b0 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Mon, 10 Jan 2022 17:03:10 -0800 Subject: [PATCH 091/120] Separate out encoder and decoder --- .../go/testwasmlibclient/service.go | 76 +++++------ .../test/testwasmlib_client_test.go | 2 +- .../vm/wasmlib/go/wasmclient/arguments.go | 111 +-------------- .../vm/wasmlib/go/wasmclient/clientfunc.go | 5 +- .../vm/wasmlib/go/wasmclient/clientview.go | 9 +- .../go/wasmclient/coreaccounts/service.go | 24 ++-- .../wasmlib/go/wasmclient/coreblob/service.go | 18 +-- .../go/wasmclient/coreblocklog/service.go | 50 +++---- .../go/wasmclient/coregovernance/service.go | 62 ++++----- .../wasmlib/go/wasmclient/coreroot/service.go | 34 ++--- packages/vm/wasmlib/go/wasmclient/decoder.go | 97 ++++++++++++++ packages/vm/wasmlib/go/wasmclient/encoder.go | 102 ++++++++++++++ packages/vm/wasmlib/go/wasmclient/results.go | 121 ++--------------- packages/vm/wasmlib/go/wasmclient/service.go | 30 ++++- packages/vm/wasmlib/go/wasmclient/transfer.go | 4 +- packages/vm/wasmlib/go/wasmclient/types.go | 3 + .../vm/wasmlib/ts/wasmclient/arguments.ts | 126 ++---------------- .../vm/wasmlib/ts/wasmclient/clientview.ts | 4 +- .../ts/wasmclient/coreaccounts/service.ts | 54 ++++---- .../wasmlib/ts/wasmclient/coreblob/service.ts | 44 +++--- .../ts/wasmclient/coreblocklog/service.ts | 110 ++++++++------- .../ts/wasmclient/coregovernance/service.ts | 86 ++++++------ .../wasmlib/ts/wasmclient/coreroot/service.ts | 34 ++--- packages/vm/wasmlib/ts/wasmclient/decoder.ts | 98 ++++++++++++++ packages/vm/wasmlib/ts/wasmclient/encoder.ts | 113 ++++++++++++++++ packages/vm/wasmlib/ts/wasmclient/index.ts | 2 + packages/vm/wasmlib/ts/wasmclient/results.ts | 121 +++-------------- packages/vm/wasmlib/ts/wasmclient/service.ts | 7 +- .../vm/wasmlib/ts/wasmclient/waspclient.ts | 9 +- .../generator/goclienttemplates/service.go | 12 +- .../generator/tsclienttemplates/service.go | 22 +-- 31 files changed, 840 insertions(+), 750 deletions(-) create mode 100644 packages/vm/wasmlib/go/wasmclient/decoder.go create mode 100644 packages/vm/wasmlib/go/wasmclient/encoder.go create mode 100644 packages/vm/wasmlib/ts/wasmclient/decoder.ts create mode 100644 packages/vm/wasmlib/ts/wasmclient/encoder.ts diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go index 44566db07b..1bb3838a12 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go @@ -51,7 +51,7 @@ type ArrayClearFunc struct { } func (f *ArrayClearFunc) Name(v string) { - f.args.SetString(ArgName, v) + f.args.Set(ArgName, f.args.FromString(v)) } func (f *ArrayClearFunc) Post() wasmclient.Request { @@ -67,7 +67,7 @@ type ArrayCreateFunc struct { } func (f *ArrayCreateFunc) Name(v string) { - f.args.SetString(ArgName, v) + f.args.Set(ArgName, f.args.FromString(v)) } func (f *ArrayCreateFunc) Post() wasmclient.Request { @@ -83,15 +83,15 @@ type ArraySetFunc struct { } func (f *ArraySetFunc) Index(v int32) { - f.args.SetInt32(ArgIndex, v) + f.args.Set(ArgIndex, f.args.FromInt32(v)) } func (f *ArraySetFunc) Name(v string) { - f.args.SetString(ArgName, v) + f.args.Set(ArgName, f.args.FromString(v)) } func (f *ArraySetFunc) Value(v string) { - f.args.SetString(ArgValue, v) + f.args.Set(ArgValue, f.args.FromString(v)) } func (f *ArraySetFunc) Post() wasmclient.Request { @@ -109,79 +109,79 @@ type ParamTypesFunc struct { } func (f *ParamTypesFunc) Address(v wasmclient.Address) { - f.args.SetAddress(ArgAddress, v) + f.args.Set(ArgAddress, f.args.FromAddress(v)) } func (f *ParamTypesFunc) AgentID(v wasmclient.AgentID) { - f.args.SetAgentID(ArgAgentID, v) + f.args.Set(ArgAgentID, f.args.FromAgentID(v)) } func (f *ParamTypesFunc) Bool(v bool) { - f.args.SetBool(ArgBool, v) + f.args.Set(ArgBool, f.args.FromBool(v)) } func (f *ParamTypesFunc) Bytes(v []byte) { - f.args.SetBytes(ArgBytes, v) + f.args.Set(ArgBytes, f.args.FromBytes(v)) } func (f *ParamTypesFunc) ChainID(v wasmclient.ChainID) { - f.args.SetChainID(ArgChainID, v) + f.args.Set(ArgChainID, f.args.FromChainID(v)) } func (f *ParamTypesFunc) Color(v wasmclient.Color) { - f.args.SetColor(ArgColor, v) + f.args.Set(ArgColor, f.args.FromColor(v)) } func (f *ParamTypesFunc) Hash(v wasmclient.Hash) { - f.args.SetHash(ArgHash, v) + f.args.Set(ArgHash, f.args.FromHash(v)) } func (f *ParamTypesFunc) Hname(v wasmclient.Hname) { - f.args.SetHname(ArgHname, v) + f.args.Set(ArgHname, f.args.FromHname(v)) } func (f *ParamTypesFunc) Int16(v int16) { - f.args.SetInt16(ArgInt16, v) + f.args.Set(ArgInt16, f.args.FromInt16(v)) } func (f *ParamTypesFunc) Int32(v int32) { - f.args.SetInt32(ArgInt32, v) + f.args.Set(ArgInt32, f.args.FromInt32(v)) } func (f *ParamTypesFunc) Int64(v int64) { - f.args.SetInt64(ArgInt64, v) + f.args.Set(ArgInt64, f.args.FromInt64(v)) } func (f *ParamTypesFunc) Int8(v int8) { - f.args.SetInt8(ArgInt8, v) + f.args.Set(ArgInt8, f.args.FromInt8(v)) } func (f *ParamTypesFunc) Param(v []byte) { - f.args.SetBytes(ArgParam, v) + f.args.Set(ArgParam, f.args.FromBytes(v)) } func (f *ParamTypesFunc) RequestID(v wasmclient.RequestID) { - f.args.SetRequestID(ArgRequestID, v) + f.args.Set(ArgRequestID, f.args.FromRequestID(v)) } func (f *ParamTypesFunc) String(v string) { - f.args.SetString(ArgString, v) + f.args.Set(ArgString, f.args.FromString(v)) } func (f *ParamTypesFunc) Uint16(v uint16) { - f.args.SetUint16(ArgUint16, v) + f.args.Set(ArgUint16, f.args.FromUint16(v)) } func (f *ParamTypesFunc) Uint32(v uint32) { - f.args.SetUint32(ArgUint32, v) + f.args.Set(ArgUint32, f.args.FromUint32(v)) } func (f *ParamTypesFunc) Uint64(v uint64) { - f.args.SetUint64(ArgUint64, v) + f.args.Set(ArgUint64, f.args.FromUint64(v)) } func (f *ParamTypesFunc) Uint8(v uint8) { - f.args.SetUint8(ArgUint8, v) + f.args.Set(ArgUint8, f.args.FromUint8(v)) } func (f *ParamTypesFunc) Post() wasmclient.Request { @@ -206,11 +206,11 @@ type TriggerEventFunc struct { } func (f *TriggerEventFunc) Address(v wasmclient.Address) { - f.args.SetAddress(ArgAddress, v) + f.args.Set(ArgAddress, f.args.FromAddress(v)) } func (f *TriggerEventFunc) Name(v string) { - f.args.SetString(ArgName, v) + f.args.Set(ArgName, f.args.FromString(v)) } func (f *TriggerEventFunc) Post() wasmclient.Request { @@ -227,7 +227,7 @@ type ArrayLengthView struct { } func (f *ArrayLengthView) Name(v string) { - f.args.SetString(ArgName, v) + f.args.Set(ArgName, f.args.FromString(v)) } func (f *ArrayLengthView) Call() ArrayLengthResults { @@ -241,7 +241,7 @@ type ArrayLengthResults struct { } func (r *ArrayLengthResults) Length() int32 { - return r.res.GetInt32(ResLength) + return r.res.ToInt32(r.res.Get(ResLength)) } ///////////////////////////// arrayValue ///////////////////////////// @@ -252,11 +252,11 @@ type ArrayValueView struct { } func (f *ArrayValueView) Index(v int32) { - f.args.SetInt32(ArgIndex, v) + f.args.Set(ArgIndex, f.args.FromInt32(v)) } func (f *ArrayValueView) Name(v string) { - f.args.SetString(ArgName, v) + f.args.Set(ArgName, f.args.FromString(v)) } func (f *ArrayValueView) Call() ArrayValueResults { @@ -271,7 +271,7 @@ type ArrayValueResults struct { } func (r *ArrayValueResults) Value() string { - return r.res.GetString(ResValue) + return r.res.ToString(r.res.Get(ResValue)) } ///////////////////////////// blockRecord ///////////////////////////// @@ -282,11 +282,11 @@ type BlockRecordView struct { } func (f *BlockRecordView) BlockIndex(v int32) { - f.args.SetInt32(ArgBlockIndex, v) + f.args.Set(ArgBlockIndex, f.args.FromInt32(v)) } func (f *BlockRecordView) RecordIndex(v int32) { - f.args.SetInt32(ArgRecordIndex, v) + f.args.Set(ArgRecordIndex, f.args.FromInt32(v)) } func (f *BlockRecordView) Call() BlockRecordResults { @@ -301,7 +301,7 @@ type BlockRecordResults struct { } func (r *BlockRecordResults) Record() []byte { - return r.res.GetBytes(ResRecord) + return r.res.ToBytes(r.res.Get(ResRecord)) } ///////////////////////////// blockRecords ///////////////////////////// @@ -312,7 +312,7 @@ type BlockRecordsView struct { } func (f *BlockRecordsView) BlockIndex(v int32) { - f.args.SetInt32(ArgBlockIndex, v) + f.args.Set(ArgBlockIndex, f.args.FromInt32(v)) } func (f *BlockRecordsView) Call() BlockRecordsResults { @@ -326,7 +326,7 @@ type BlockRecordsResults struct { } func (r *BlockRecordsResults) Count() int32 { - return r.res.GetInt32(ResCount) + return r.res.ToInt32(r.res.Get(ResCount)) } ///////////////////////////// getRandom ///////////////////////////// @@ -345,7 +345,7 @@ type GetRandomResults struct { } func (r *GetRandomResults) Random() int64 { - return r.res.GetInt64(ResRandom) + return r.res.ToInt64(r.res.Get(ResRandom)) } ///////////////////////////// iotaBalance ///////////////////////////// @@ -364,7 +364,7 @@ type IotaBalanceResults struct { } func (r *IotaBalanceResults) Iotas() int64 { - return r.res.GetInt64(ResIotas) + return r.res.ToInt64(r.res.Get(ResIotas)) } ///////////////////////////// TestWasmLibService ///////////////////////////// diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go index 0555b0c90a..24420f6964 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go @@ -15,8 +15,8 @@ import ( // the contract has already been deployed in some way, so // these values are usually available from elsewhere const ( + myChainID = "tRA59jhoDG9QpXi7NcQ3phuQaaPqgXRBgXxK2Sy1FPGf" mySeed = "6C6tRksZDWeDTCzX4Q7R2hbpyFV86cSGLVxdkFKSB3sv" - myChainID = "j2mrizabjDRPwU2eU37yXFLcgCHCwVUD79v2ufWbBFm4" ) func setupClient(t *testing.T) *testwasmlibclient.TestWasmLibService { diff --git a/packages/vm/wasmlib/go/wasmclient/arguments.go b/packages/vm/wasmlib/go/wasmclient/arguments.go index a06e391488..bea1f46bfb 100644 --- a/packages/vm/wasmlib/go/wasmclient/arguments.go +++ b/packages/vm/wasmlib/go/wasmclient/arguments.go @@ -5,30 +5,20 @@ package wasmclient import ( "strconv" - - "github.com/iotaledger/wasp/packages/kv" - "github.com/iotaledger/wasp/packages/kv/dict" ) // The Arguments struct is used to gather all arguments for a smart // contract function call and encode it into a deterministic byte array type Arguments struct { - args dict.Dict + Encoder + args ArgMap } -func (a *Arguments) set(key string, val []byte) { +func (a *Arguments) Set(key string, val []byte) { if a.args == nil { - a.args = make(dict.Dict) - } - a.args[kv.Key(key)] = val -} - -func (a *Arguments) setBase58(key, val string, typeID int32) { - bytes := Base58Decode(val) - if len(bytes) != int(TypeSizes[typeID]) { - panic("invalid byte size") + a.args = make(ArgMap) } - a.set(key, bytes) + a.args.Set(key, val) } func (a *Arguments) IndexedKey(key string, index int) string { @@ -36,95 +26,8 @@ func (a *Arguments) IndexedKey(key string, index int) string { } func (a *Arguments) Mandatory(key string) { - if a.args != nil { - if _, ok := a.args[kv.Key(key)]; ok { - return - } + if a.args != nil && a.args.Get(key) != nil { + return } panic("missing mandatory " + key) } - -func (a *Arguments) SetAddress(key string, val Address) { - a.setBase58(key, string(val), TYPE_ADDRESS) -} - -func (a *Arguments) SetAgentID(key string, val AgentID) { - a.setBase58(key, string(val), TYPE_AGENT_ID) -} - -func (a *Arguments) SetBool(key string, val bool) { - bytes := []byte{0} - if val { - bytes[0] = 1 - } - a.set(key, bytes) -} - -func (a *Arguments) SetBytes(key string, val []byte) { - a.set(key, val) -} - -func (a *Arguments) SetColor(key string, val Color) { - a.setBase58(key, string(val), TYPE_COLOR) -} - -func (a *Arguments) SetChainID(key string, val ChainID) { - a.setBase58(key, string(val), TYPE_CHAIN_ID) -} - -func (a *Arguments) SetHash(key string, val Hash) { - a.setBase58(key, string(val), TYPE_HASH) -} - -func (a *Arguments) SetHname(key string, val Hname) { - a.SetUint32(key, uint32(val)) -} - -func (a *Arguments) SetInt8(key string, val int8) { - a.set(key, []byte{byte(val)}) -} - -func (a *Arguments) SetInt16(key string, val int16) { - a.setUint64(key, uint64(val), 2) -} - -func (a *Arguments) SetInt32(key string, val int32) { - a.setUint64(key, uint64(val), 4) -} - -func (a *Arguments) SetInt64(key string, val int64) { - a.setUint64(key, uint64(val), 4) -} - -func (a *Arguments) SetRequestID(key string, val RequestID) { - a.setBase58(key, string(val), TYPE_REQUEST_ID) -} - -func (a *Arguments) SetString(key, val string) { - a.set(key, []byte(val)) -} - -func (a *Arguments) SetUint8(key string, val uint8) { - a.set(key, []byte{val}) -} - -func (a *Arguments) SetUint16(key string, val uint16) { - a.setUint64(key, uint64(val), 2) -} - -func (a *Arguments) SetUint32(key string, val uint32) { - a.setUint64(key, uint64(val), 4) -} - -func (a *Arguments) SetUint64(key string, val uint64) { - a.setUint64(key, val, 4) -} - -func (a *Arguments) setUint64(key string, val uint64, size int) { - bytes := make([]byte, size) - for i := 0; i < size; i++ { - bytes[i] = byte(val) - val >>= 8 - } - a.set(key, bytes) -} diff --git a/packages/vm/wasmlib/go/wasmclient/clientfunc.go b/packages/vm/wasmlib/go/wasmclient/clientfunc.go index 6adc8e144c..07654bbc88 100644 --- a/packages/vm/wasmlib/go/wasmclient/clientfunc.go +++ b/packages/vm/wasmlib/go/wasmclient/clientfunc.go @@ -25,7 +25,10 @@ func (f *ClientFunc) Post(hFuncName uint32, args *Arguments) Request { if keyPair == nil { keyPair = f.svc.keyPair } - return f.svc.PostRequest(hFuncName, args, f.xfer, keyPair, f.onLedger) + if args == nil { + args = &Arguments{} + } + return f.svc.PostRequest(hFuncName, args.args, f.xfer, keyPair, f.onLedger) } // Sign optionally overrides the default keypair from the service diff --git a/packages/vm/wasmlib/go/wasmclient/clientview.go b/packages/vm/wasmlib/go/wasmclient/clientview.go index bb70b27321..bd0ceed8a2 100644 --- a/packages/vm/wasmlib/go/wasmclient/clientview.go +++ b/packages/vm/wasmlib/go/wasmclient/clientview.go @@ -3,16 +3,17 @@ package wasmclient -import "github.com/iotaledger/wasp/packages/kv/dict" - type ClientView struct { svc *Service err error - res dict.Dict + res ResMap } func (v *ClientView) Call(viewName string, args *Arguments) { - v.res, v.err = v.svc.CallView(viewName, args) + if args == nil { + args = &Arguments{} + } + v.res, v.err = v.svc.CallView(viewName, args.args) } func (v *ClientView) Error() error { diff --git a/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go b/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go index fb343bcad0..269b5a35ed 100644 --- a/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coreaccounts/service.go @@ -27,7 +27,7 @@ type DepositFunc struct { } func (f *DepositFunc) AgentID(v wasmclient.AgentID) { - f.args.SetAgentID(ArgAgentID, v) + f.args.Set(ArgAgentID, f.args.FromAgentID(v)) } func (f *DepositFunc) Post() wasmclient.Request { @@ -42,11 +42,11 @@ type HarvestFunc struct { } func (f *HarvestFunc) WithdrawAmount(v int64) { - f.args.SetInt64(ArgWithdrawAmount, v) + f.args.Set(ArgWithdrawAmount, f.args.FromInt64(v)) } func (f *HarvestFunc) WithdrawColor(v wasmclient.Color) { - f.args.SetColor(ArgWithdrawColor, v) + f.args.Set(ArgWithdrawColor, f.args.FromColor(v)) } func (f *HarvestFunc) Post() wasmclient.Request { @@ -80,8 +80,8 @@ type AccountsResults struct { func (r *AccountsResults) Agents() map[wasmclient.AgentID][]byte { res := make(map[wasmclient.AgentID][]byte) - r.res.ForEach(func(key string, val string) { - res[wasmclient.AgentID(key)] = r.res.GetBytes(val) + r.res.ForEach(func(key []byte, val []byte) { + res[r.res.ToAgentID(key)] = r.res.ToBytes(val) }) return res } @@ -94,7 +94,7 @@ type BalanceView struct { } func (f *BalanceView) AgentID(v wasmclient.AgentID) { - f.args.SetAgentID(ArgAgentID, v) + f.args.Set(ArgAgentID, f.args.FromAgentID(v)) } func (f *BalanceView) Call() BalanceResults { @@ -109,8 +109,8 @@ type BalanceResults struct { func (r *BalanceResults) Balances() map[wasmclient.Color]int64 { res := make(map[wasmclient.Color]int64) - r.res.ForEach(func(key string, val string) { - res[wasmclient.Color(key)] = r.res.GetInt64(val) + r.res.ForEach(func(key []byte, val []byte) { + res[r.res.ToColor(key)] = r.res.ToInt64(val) }) return res } @@ -123,7 +123,7 @@ type GetAccountNonceView struct { } func (f *GetAccountNonceView) AgentID(v wasmclient.AgentID) { - f.args.SetAgentID(ArgAgentID, v) + f.args.Set(ArgAgentID, f.args.FromAgentID(v)) } func (f *GetAccountNonceView) Call() GetAccountNonceResults { @@ -137,7 +137,7 @@ type GetAccountNonceResults struct { } func (r *GetAccountNonceResults) AccountNonce() int64 { - return r.res.GetInt64(ResAccountNonce) + return r.res.ToInt64(r.res.Get(ResAccountNonce)) } ///////////////////////////// totalAssets ///////////////////////////// @@ -157,8 +157,8 @@ type TotalAssetsResults struct { func (r *TotalAssetsResults) Balances() map[wasmclient.Color]int64 { res := make(map[wasmclient.Color]int64) - r.res.ForEach(func(key string, val string) { - res[wasmclient.Color(key)] = r.res.GetInt64(val) + r.res.ForEach(func(key []byte, val []byte) { + res[r.res.ToColor(key)] = r.res.ToInt64(val) }) return res } diff --git a/packages/vm/wasmlib/go/wasmclient/coreblob/service.go b/packages/vm/wasmlib/go/wasmclient/coreblob/service.go index 0e5bca9d5c..208046b63e 100644 --- a/packages/vm/wasmlib/go/wasmclient/coreblob/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coreblob/service.go @@ -27,7 +27,7 @@ type StoreBlobFunc struct { } func (f *StoreBlobFunc) Blobs(v []byte) { - f.args.SetBytes(ArgBlobs, v) + f.args.Set(ArgBlobs, f.args.FromBytes(v)) } func (f *StoreBlobFunc) Post() wasmclient.Request { @@ -42,11 +42,11 @@ type GetBlobFieldView struct { } func (f *GetBlobFieldView) Field(v string) { - f.args.SetString(ArgField, v) + f.args.Set(ArgField, f.args.FromString(v)) } func (f *GetBlobFieldView) Hash(v wasmclient.Hash) { - f.args.SetHash(ArgHash, v) + f.args.Set(ArgHash, f.args.FromHash(v)) } func (f *GetBlobFieldView) Call() GetBlobFieldResults { @@ -61,7 +61,7 @@ type GetBlobFieldResults struct { } func (r *GetBlobFieldResults) Bytes() []byte { - return r.res.GetBytes(ResBytes) + return r.res.ToBytes(r.res.Get(ResBytes)) } ///////////////////////////// getBlobInfo ///////////////////////////// @@ -72,7 +72,7 @@ type GetBlobInfoView struct { } func (f *GetBlobInfoView) Hash(v wasmclient.Hash) { - f.args.SetHash(ArgHash, v) + f.args.Set(ArgHash, f.args.FromHash(v)) } func (f *GetBlobInfoView) Call() GetBlobInfoResults { @@ -87,8 +87,8 @@ type GetBlobInfoResults struct { func (r *GetBlobInfoResults) BlobSizes() map[string]int32 { res := make(map[string]int32) - r.res.ForEach(func(key string, val string) { - res[string(key)] = r.res.GetInt32(val) + r.res.ForEach(func(key []byte, val []byte) { + res[r.res.ToString(key)] = r.res.ToInt32(val) }) return res } @@ -110,8 +110,8 @@ type ListBlobsResults struct { func (r *ListBlobsResults) BlobSizes() map[wasmclient.Hash]int32 { res := make(map[wasmclient.Hash]int32) - r.res.ForEach(func(key string, val string) { - res[wasmclient.Hash(key)] = r.res.GetInt32(val) + r.res.ForEach(func(key []byte, val []byte) { + res[r.res.ToHash(key)] = r.res.ToInt32(val) }) return res } diff --git a/packages/vm/wasmlib/go/wasmclient/coreblocklog/service.go b/packages/vm/wasmlib/go/wasmclient/coreblocklog/service.go index d270202d2b..ff8101b85f 100644 --- a/packages/vm/wasmlib/go/wasmclient/coreblocklog/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coreblocklog/service.go @@ -43,15 +43,15 @@ type ControlAddressesResults struct { } func (r *ControlAddressesResults) BlockIndex() int32 { - return r.res.GetInt32(ResBlockIndex) + return r.res.ToInt32(r.res.Get(ResBlockIndex)) } func (r *ControlAddressesResults) GoverningAddress() wasmclient.Address { - return r.res.GetAddress(ResGoverningAddress) + return r.res.ToAddress(r.res.Get(ResGoverningAddress)) } func (r *ControlAddressesResults) StateControllerAddress() wasmclient.Address { - return r.res.GetAddress(ResStateControllerAddress) + return r.res.ToAddress(r.res.Get(ResStateControllerAddress)) } ///////////////////////////// getBlockInfo ///////////////////////////// @@ -62,7 +62,7 @@ type GetBlockInfoView struct { } func (f *GetBlockInfoView) BlockIndex(v int32) { - f.args.SetInt32(ArgBlockIndex, v) + f.args.Set(ArgBlockIndex, f.args.FromInt32(v)) } func (f *GetBlockInfoView) Call() GetBlockInfoResults { @@ -76,7 +76,7 @@ type GetBlockInfoResults struct { } func (r *GetBlockInfoResults) BlockInfo() []byte { - return r.res.GetBytes(ResBlockInfo) + return r.res.ToBytes(r.res.Get(ResBlockInfo)) } ///////////////////////////// getEventsForBlock ///////////////////////////// @@ -87,7 +87,7 @@ type GetEventsForBlockView struct { } func (f *GetEventsForBlockView) BlockIndex(v int32) { - f.args.SetInt32(ArgBlockIndex, v) + f.args.Set(ArgBlockIndex, f.args.FromInt32(v)) } func (f *GetEventsForBlockView) Call() GetEventsForBlockResults { @@ -101,7 +101,7 @@ type GetEventsForBlockResults struct { } func (r *GetEventsForBlockResults) Event() []byte { - return r.res.GetBytes(ResEvent) + return r.res.ToBytes(r.res.Get(ResEvent)) } ///////////////////////////// getEventsForContract ///////////////////////////// @@ -112,15 +112,15 @@ type GetEventsForContractView struct { } func (f *GetEventsForContractView) ContractHname(v wasmclient.Hname) { - f.args.SetHname(ArgContractHname, v) + f.args.Set(ArgContractHname, f.args.FromHname(v)) } func (f *GetEventsForContractView) FromBlock(v int32) { - f.args.SetInt32(ArgFromBlock, v) + f.args.Set(ArgFromBlock, f.args.FromInt32(v)) } func (f *GetEventsForContractView) ToBlock(v int32) { - f.args.SetInt32(ArgToBlock, v) + f.args.Set(ArgToBlock, f.args.FromInt32(v)) } func (f *GetEventsForContractView) Call() GetEventsForContractResults { @@ -134,7 +134,7 @@ type GetEventsForContractResults struct { } func (r *GetEventsForContractResults) Event() []byte { - return r.res.GetBytes(ResEvent) + return r.res.ToBytes(r.res.Get(ResEvent)) } ///////////////////////////// getEventsForRequest ///////////////////////////// @@ -145,7 +145,7 @@ type GetEventsForRequestView struct { } func (f *GetEventsForRequestView) RequestID(v wasmclient.RequestID) { - f.args.SetRequestID(ArgRequestID, v) + f.args.Set(ArgRequestID, f.args.FromRequestID(v)) } func (f *GetEventsForRequestView) Call() GetEventsForRequestResults { @@ -159,7 +159,7 @@ type GetEventsForRequestResults struct { } func (r *GetEventsForRequestResults) Event() []byte { - return r.res.GetBytes(ResEvent) + return r.res.ToBytes(r.res.Get(ResEvent)) } ///////////////////////////// getLatestBlockInfo ///////////////////////////// @@ -178,11 +178,11 @@ type GetLatestBlockInfoResults struct { } func (r *GetLatestBlockInfoResults) BlockIndex() int32 { - return r.res.GetInt32(ResBlockIndex) + return r.res.ToInt32(r.res.Get(ResBlockIndex)) } func (r *GetLatestBlockInfoResults) BlockInfo() []byte { - return r.res.GetBytes(ResBlockInfo) + return r.res.ToBytes(r.res.Get(ResBlockInfo)) } ///////////////////////////// getRequestIDsForBlock ///////////////////////////// @@ -193,7 +193,7 @@ type GetRequestIDsForBlockView struct { } func (f *GetRequestIDsForBlockView) BlockIndex(v int32) { - f.args.SetInt32(ArgBlockIndex, v) + f.args.Set(ArgBlockIndex, f.args.FromInt32(v)) } func (f *GetRequestIDsForBlockView) Call() GetRequestIDsForBlockResults { @@ -207,7 +207,7 @@ type GetRequestIDsForBlockResults struct { } func (r *GetRequestIDsForBlockResults) RequestID() wasmclient.RequestID { - return r.res.GetRequestID(ResRequestID) + return r.res.ToRequestID(r.res.Get(ResRequestID)) } ///////////////////////////// getRequestReceipt ///////////////////////////// @@ -218,7 +218,7 @@ type GetRequestReceiptView struct { } func (f *GetRequestReceiptView) RequestID(v wasmclient.RequestID) { - f.args.SetRequestID(ArgRequestID, v) + f.args.Set(ArgRequestID, f.args.FromRequestID(v)) } func (f *GetRequestReceiptView) Call() GetRequestReceiptResults { @@ -232,15 +232,15 @@ type GetRequestReceiptResults struct { } func (r *GetRequestReceiptResults) BlockIndex() int32 { - return r.res.GetInt32(ResBlockIndex) + return r.res.ToInt32(r.res.Get(ResBlockIndex)) } func (r *GetRequestReceiptResults) RequestIndex() int16 { - return r.res.GetInt16(ResRequestIndex) + return r.res.ToInt16(r.res.Get(ResRequestIndex)) } func (r *GetRequestReceiptResults) RequestRecord() []byte { - return r.res.GetBytes(ResRequestRecord) + return r.res.ToBytes(r.res.Get(ResRequestRecord)) } ///////////////////////////// getRequestReceiptsForBlock ///////////////////////////// @@ -251,7 +251,7 @@ type GetRequestReceiptsForBlockView struct { } func (f *GetRequestReceiptsForBlockView) BlockIndex(v int32) { - f.args.SetInt32(ArgBlockIndex, v) + f.args.Set(ArgBlockIndex, f.args.FromInt32(v)) } func (f *GetRequestReceiptsForBlockView) Call() GetRequestReceiptsForBlockResults { @@ -265,7 +265,7 @@ type GetRequestReceiptsForBlockResults struct { } func (r *GetRequestReceiptsForBlockResults) RequestRecord() []byte { - return r.res.GetBytes(ResRequestRecord) + return r.res.ToBytes(r.res.Get(ResRequestRecord)) } ///////////////////////////// isRequestProcessed ///////////////////////////// @@ -276,7 +276,7 @@ type IsRequestProcessedView struct { } func (f *IsRequestProcessedView) RequestID(v wasmclient.RequestID) { - f.args.SetRequestID(ArgRequestID, v) + f.args.Set(ArgRequestID, f.args.FromRequestID(v)) } func (f *IsRequestProcessedView) Call() IsRequestProcessedResults { @@ -290,7 +290,7 @@ type IsRequestProcessedResults struct { } func (r *IsRequestProcessedResults) RequestProcessed() string { - return r.res.GetString(ResRequestProcessed) + return r.res.ToString(r.res.Get(ResRequestProcessed)) } ///////////////////////////// CoreBlockLogService ///////////////////////////// diff --git a/packages/vm/wasmlib/go/wasmclient/coregovernance/service.go b/packages/vm/wasmlib/go/wasmclient/coregovernance/service.go index 3bb94cfb70..a3f5add5f3 100644 --- a/packages/vm/wasmlib/go/wasmclient/coregovernance/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coregovernance/service.go @@ -42,15 +42,15 @@ type AddAllowedStateControllerAddressFunc struct { } func (f *AddAllowedStateControllerAddressFunc) ChainOwner(v wasmclient.AgentID) { - f.args.SetAgentID(ArgChainOwner, v) + f.args.Set(ArgChainOwner, f.args.FromAgentID(v)) } func (f *AddAllowedStateControllerAddressFunc) FeeColor(v wasmclient.Color) { - f.args.SetColor(ArgFeeColor, v) + f.args.Set(ArgFeeColor, f.args.FromColor(v)) } func (f *AddAllowedStateControllerAddressFunc) StateControllerAddress(v wasmclient.Address) { - f.args.SetAddress(ArgStateControllerAddress, v) + f.args.Set(ArgStateControllerAddress, f.args.FromAddress(v)) } func (f *AddAllowedStateControllerAddressFunc) Post() wasmclient.Request { @@ -77,7 +77,7 @@ type DelegateChainOwnershipFunc struct { } func (f *DelegateChainOwnershipFunc) ChainOwner(v wasmclient.AgentID) { - f.args.SetAgentID(ArgChainOwner, v) + f.args.Set(ArgChainOwner, f.args.FromAgentID(v)) } func (f *DelegateChainOwnershipFunc) Post() wasmclient.Request { @@ -93,7 +93,7 @@ type RemoveAllowedStateControllerAddressFunc struct { } func (f *RemoveAllowedStateControllerAddressFunc) StateControllerAddress(v wasmclient.Address) { - f.args.SetAddress(ArgStateControllerAddress, v) + f.args.Set(ArgStateControllerAddress, f.args.FromAddress(v)) } func (f *RemoveAllowedStateControllerAddressFunc) Post() wasmclient.Request { @@ -109,7 +109,7 @@ type RotateStateControllerFunc struct { } func (f *RotateStateControllerFunc) StateControllerAddress(v wasmclient.Address) { - f.args.SetAddress(ArgStateControllerAddress, v) + f.args.Set(ArgStateControllerAddress, f.args.FromAddress(v)) } func (f *RotateStateControllerFunc) Post() wasmclient.Request { @@ -125,23 +125,23 @@ type SetChainInfoFunc struct { } func (f *SetChainInfoFunc) MaxBlobSize(v int32) { - f.args.SetInt32(ArgMaxBlobSize, v) + f.args.Set(ArgMaxBlobSize, f.args.FromInt32(v)) } func (f *SetChainInfoFunc) MaxEventSize(v int16) { - f.args.SetInt16(ArgMaxEventSize, v) + f.args.Set(ArgMaxEventSize, f.args.FromInt16(v)) } func (f *SetChainInfoFunc) MaxEventsPerReq(v int16) { - f.args.SetInt16(ArgMaxEventsPerReq, v) + f.args.Set(ArgMaxEventsPerReq, f.args.FromInt16(v)) } func (f *SetChainInfoFunc) OwnerFee(v int64) { - f.args.SetInt64(ArgOwnerFee, v) + f.args.Set(ArgOwnerFee, f.args.FromInt64(v)) } func (f *SetChainInfoFunc) ValidatorFee(v int64) { - f.args.SetInt64(ArgValidatorFee, v) + f.args.Set(ArgValidatorFee, f.args.FromInt64(v)) } func (f *SetChainInfoFunc) Post() wasmclient.Request { @@ -156,15 +156,15 @@ type SetContractFeeFunc struct { } func (f *SetContractFeeFunc) Hname(v wasmclient.Hname) { - f.args.SetHname(ArgHname, v) + f.args.Set(ArgHname, f.args.FromHname(v)) } func (f *SetContractFeeFunc) OwnerFee(v int64) { - f.args.SetInt64(ArgOwnerFee, v) + f.args.Set(ArgOwnerFee, f.args.FromInt64(v)) } func (f *SetContractFeeFunc) ValidatorFee(v int64) { - f.args.SetInt64(ArgValidatorFee, v) + f.args.Set(ArgValidatorFee, f.args.FromInt64(v)) } func (f *SetContractFeeFunc) Post() wasmclient.Request { @@ -180,11 +180,11 @@ type SetDefaultFeeFunc struct { } func (f *SetDefaultFeeFunc) OwnerFee(v int64) { - f.args.SetInt64(ArgOwnerFee, v) + f.args.Set(ArgOwnerFee, f.args.FromInt64(v)) } func (f *SetDefaultFeeFunc) ValidatorFee(v int64) { - f.args.SetInt64(ArgValidatorFee, v) + f.args.Set(ArgValidatorFee, f.args.FromInt64(v)) } func (f *SetDefaultFeeFunc) Post() wasmclient.Request { @@ -207,7 +207,7 @@ type GetAllowedStateControllerAddressesResults struct { } func (r *GetAllowedStateControllerAddressesResults) AllowedStateControllerAddresses() []byte { - return r.res.GetBytes(ResAllowedStateControllerAddresses) + return r.res.ToBytes(r.res.Get(ResAllowedStateControllerAddresses)) } ///////////////////////////// getChainInfo ///////////////////////////// @@ -226,39 +226,39 @@ type GetChainInfoResults struct { } func (r *GetChainInfoResults) ChainID() wasmclient.ChainID { - return r.res.GetChainID(ResChainID) + return r.res.ToChainID(r.res.Get(ResChainID)) } func (r *GetChainInfoResults) ChainOwnerID() wasmclient.AgentID { - return r.res.GetAgentID(ResChainOwnerID) + return r.res.ToAgentID(r.res.Get(ResChainOwnerID)) } func (r *GetChainInfoResults) DefaultOwnerFee() int64 { - return r.res.GetInt64(ResDefaultOwnerFee) + return r.res.ToInt64(r.res.Get(ResDefaultOwnerFee)) } func (r *GetChainInfoResults) DefaultValidatorFee() int64 { - return r.res.GetInt64(ResDefaultValidatorFee) + return r.res.ToInt64(r.res.Get(ResDefaultValidatorFee)) } func (r *GetChainInfoResults) Description() string { - return r.res.GetString(ResDescription) + return r.res.ToString(r.res.Get(ResDescription)) } func (r *GetChainInfoResults) FeeColor() wasmclient.Color { - return r.res.GetColor(ResFeeColor) + return r.res.ToColor(r.res.Get(ResFeeColor)) } func (r *GetChainInfoResults) MaxBlobSize() int32 { - return r.res.GetInt32(ResMaxBlobSize) + return r.res.ToInt32(r.res.Get(ResMaxBlobSize)) } func (r *GetChainInfoResults) MaxEventSize() int16 { - return r.res.GetInt16(ResMaxEventSize) + return r.res.ToInt16(r.res.Get(ResMaxEventSize)) } func (r *GetChainInfoResults) MaxEventsPerReq() int16 { - return r.res.GetInt16(ResMaxEventsPerReq) + return r.res.ToInt16(r.res.Get(ResMaxEventsPerReq)) } ///////////////////////////// getFeeInfo ///////////////////////////// @@ -269,7 +269,7 @@ type GetFeeInfoView struct { } func (f *GetFeeInfoView) Hname(v wasmclient.Hname) { - f.args.SetHname(ArgHname, v) + f.args.Set(ArgHname, f.args.FromHname(v)) } func (f *GetFeeInfoView) Call() GetFeeInfoResults { @@ -283,15 +283,15 @@ type GetFeeInfoResults struct { } func (r *GetFeeInfoResults) FeeColor() wasmclient.Color { - return r.res.GetColor(ResFeeColor) + return r.res.ToColor(r.res.Get(ResFeeColor)) } func (r *GetFeeInfoResults) OwnerFee() int64 { - return r.res.GetInt64(ResOwnerFee) + return r.res.ToInt64(r.res.Get(ResOwnerFee)) } func (r *GetFeeInfoResults) ValidatorFee() int64 { - return r.res.GetInt64(ResValidatorFee) + return r.res.ToInt64(r.res.Get(ResValidatorFee)) } ///////////////////////////// getMaxBlobSize ///////////////////////////// @@ -310,7 +310,7 @@ type GetMaxBlobSizeResults struct { } func (r *GetMaxBlobSizeResults) MaxBlobSize() int32 { - return r.res.GetInt32(ResMaxBlobSize) + return r.res.ToInt32(r.res.Get(ResMaxBlobSize)) } ///////////////////////////// CoreGovernanceService ///////////////////////////// diff --git a/packages/vm/wasmlib/go/wasmclient/coreroot/service.go b/packages/vm/wasmlib/go/wasmclient/coreroot/service.go index a6ade78ed4..735d0e0968 100644 --- a/packages/vm/wasmlib/go/wasmclient/coreroot/service.go +++ b/packages/vm/wasmlib/go/wasmclient/coreroot/service.go @@ -10,14 +10,14 @@ package corerootclient import "github.com/iotaledger/wasp/packages/vm/wasmlib/go/wasmclient" const ( - ArgDeployer = "dp" + ArgDeployer = "dp" ArgDescription = "ds" - ArgHname = "hn" - ArgName = "nm" + ArgHname = "hn" + ArgName = "nm" ArgProgramHash = "ph" - ResContractFound = "cf" - ResContractRecData = "dt" + ResContractFound = "cf" + ResContractRecData = "dt" ResContractRegistry = "r" ) @@ -29,15 +29,15 @@ type DeployContractFunc struct { } func (f *DeployContractFunc) Description(v string) { - f.args.SetString(ArgDescription, v) + f.args.Set(ArgDescription, f.args.FromString(v)) } func (f *DeployContractFunc) Name(v string) { - f.args.SetString(ArgName, v) + f.args.Set(ArgName, f.args.FromString(v)) } func (f *DeployContractFunc) ProgramHash(v wasmclient.Hash) { - f.args.SetHash(ArgProgramHash, v) + f.args.Set(ArgProgramHash, f.args.FromHash(v)) } func (f *DeployContractFunc) Post() wasmclient.Request { @@ -54,7 +54,7 @@ type GrantDeployPermissionFunc struct { } func (f *GrantDeployPermissionFunc) Deployer(v wasmclient.AgentID) { - f.args.SetAgentID(ArgDeployer, v) + f.args.Set(ArgDeployer, f.args.FromAgentID(v)) } func (f *GrantDeployPermissionFunc) Post() wasmclient.Request { @@ -70,7 +70,7 @@ type RevokeDeployPermissionFunc struct { } func (f *RevokeDeployPermissionFunc) Deployer(v wasmclient.AgentID) { - f.args.SetAgentID(ArgDeployer, v) + f.args.Set(ArgDeployer, f.args.FromAgentID(v)) } func (f *RevokeDeployPermissionFunc) Post() wasmclient.Request { @@ -86,7 +86,7 @@ type FindContractView struct { } func (f *FindContractView) Hname(v wasmclient.Hname) { - f.args.SetHname(ArgHname, v) + f.args.Set(ArgHname, f.args.FromHname(v)) } func (f *FindContractView) Call() FindContractResults { @@ -100,11 +100,11 @@ type FindContractResults struct { } func (r *FindContractResults) ContractFound() []byte { - return r.res.GetBytes(ResContractFound) + return r.res.ToBytes(r.res.Get(ResContractFound)) } func (r *FindContractResults) ContractRecData() []byte { - return r.res.GetBytes(ResContractRecData) + return r.res.ToBytes(r.res.Get(ResContractRecData)) } ///////////////////////////// getContractRecords ///////////////////////////// @@ -123,10 +123,10 @@ type GetContractRecordsResults struct { } func (r *GetContractRecordsResults) ContractRegistry() map[wasmclient.Hname][]byte { - res := make(map[wasmclient.Hname][]byte) - r.res.ForEach(func(key string, val string) { - res[wasmclient.Hname(key)] = r.res.GetBytes(val) - }) + res := make(map[wasmclient.Hname][]byte) + r.res.ForEach(func(key []byte, val []byte) { + res[r.res.ToHname(key)] = r.res.ToBytes(val) + }) return res } diff --git a/packages/vm/wasmlib/go/wasmclient/decoder.go b/packages/vm/wasmlib/go/wasmclient/decoder.go new file mode 100644 index 0000000000..11d17e749f --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/decoder.go @@ -0,0 +1,97 @@ +package wasmclient + +import "encoding/binary" + +type Decoder struct{} + +func checkDefault(bytes []byte, typeID int32) []byte { + size := TypeSizes[typeID] + if bytes == nil { + // return default all-zero bytes value + return make([]byte, size) + } + if size != 0 && len(bytes) != int(size) { + panic("invalid type size") + } + return bytes +} + +func toBase58(bytes []byte, typeID int32) string { + return Base58Encode(checkDefault(bytes, typeID)) +} + +func (c Decoder) ToAddress(bytes []byte) Address { + return Address(toBase58(bytes, TYPE_ADDRESS)) +} + +func (c Decoder) ToAgentID(bytes []byte) AgentID { + return AgentID(toBase58(bytes, TYPE_AGENT_ID)) +} + +func (c Decoder) ToBool(bytes []byte) bool { + return checkDefault(bytes, TYPE_BOOL)[0] != 0 +} + +func (c Decoder) ToBytes(bytes []byte) []byte { + return checkDefault(bytes, TYPE_BYTES) +} + +func (c Decoder) ToChainID(bytes []byte) ChainID { + return ChainID(toBase58(bytes, TYPE_CHAIN_ID)) +} + +func (c Decoder) ToColor(bytes []byte) Color { + color := toBase58(bytes, TYPE_COLOR) + if color == COLOR_IOTA_BASE58 { + color = COLOR_IOTA + } + return Color(color) +} + +func (c Decoder) ToHash(bytes []byte) Hash { + return Hash(toBase58(bytes, TYPE_HASH)) +} + +func (c Decoder) ToHname(bytes []byte) Hname { + return Hname(binary.LittleEndian.Uint32(checkDefault(bytes, TYPE_HNAME))) +} + +func (c Decoder) ToInt8(bytes []byte) int8 { + return int8(c.ToUint8(bytes)) +} + +func (c Decoder) ToInt16(bytes []byte) int16 { + return int16(c.ToUint16(bytes)) +} + +func (c Decoder) ToInt32(bytes []byte) int32 { + return int32(c.ToUint32(bytes)) +} + +func (c Decoder) ToInt64(bytes []byte) int64 { + return int64(c.ToUint64(bytes)) +} + +func (c Decoder) ToRequestID(bytes []byte) RequestID { + return RequestID(toBase58(bytes, TYPE_REQUEST_ID)) +} + +func (c Decoder) ToString(bytes []byte) string { + return string(checkDefault(bytes, TYPE_STRING)) +} + +func (c Decoder) ToUint8(bytes []byte) uint8 { + return checkDefault(bytes, TYPE_INT8)[0] +} + +func (c Decoder) ToUint16(bytes []byte) uint16 { + return binary.LittleEndian.Uint16(checkDefault(bytes, TYPE_INT16)) +} + +func (c Decoder) ToUint32(bytes []byte) uint32 { + return binary.LittleEndian.Uint32(checkDefault(bytes, TYPE_INT32)) +} + +func (c Decoder) ToUint64(bytes []byte) uint64 { + return binary.LittleEndian.Uint64(checkDefault(bytes, TYPE_INT64)) +} diff --git a/packages/vm/wasmlib/go/wasmclient/encoder.go b/packages/vm/wasmlib/go/wasmclient/encoder.go new file mode 100644 index 0000000000..3f4ce6f227 --- /dev/null +++ b/packages/vm/wasmlib/go/wasmclient/encoder.go @@ -0,0 +1,102 @@ +package wasmclient + +import "encoding/binary" + +type Encoder struct{} + +func fromBase58(value string, typeID int32) []byte { + bytes := Base58Decode(value) + if len(bytes) != int(TypeSizes[typeID]) { + panic("invalid byte size") + } + return bytes +} + +func (c Encoder) FromAddress(value Address) []byte { + return fromBase58(string(value), TYPE_ADDRESS) +} + +func (c Encoder) FromAgentID(value AgentID) []byte { + return fromBase58(string(value), TYPE_AGENT_ID) +} + +func (c Encoder) FromBool(value bool) []byte { + bytes := []byte{0} + if value { + bytes[0] = 1 + } + return bytes +} + +func (c Encoder) FromBytes(value []byte) []byte { + if value == nil { + panic("missing mandatory byte array") + } + return value +} + +func (c Encoder) FromChainID(value ChainID) []byte { + return fromBase58(string(value), TYPE_CHAIN_ID) +} + +func (c Encoder) FromColor(value Color) []byte { + color := string(value) + if color == COLOR_IOTA { + color = COLOR_IOTA_BASE58 + } + return fromBase58(color, TYPE_COLOR) +} + +func (c Encoder) FromHash(value Hash) []byte { + return fromBase58(string(value), TYPE_HASH) +} + +func (c Encoder) FromHname(value Hname) []byte { + return c.FromUint32(uint32(value)) +} + +func (c Encoder) FromInt8(value int8) []byte { + return c.FromUint8(uint8(value)) +} + +func (c Encoder) FromInt16(value int16) []byte { + return c.FromUint16(uint16(value)) +} + +func (c Encoder) FromInt32(value int32) []byte { + return c.FromUint32(uint32(value)) +} + +func (c Encoder) FromInt64(value int64) []byte { + return c.FromUint64(uint64(value)) +} + +func (c Encoder) FromRequestID(value RequestID) []byte { + return fromBase58(string(value), TYPE_REQUEST_ID) +} + +func (c Encoder) FromString(value string) []byte { + return []byte(value) +} + +func (c Encoder) FromUint8(value uint8) []byte { + return []byte{value} +} + +func (c Encoder) FromUint16(value uint16) []byte { + bytes := make([]byte, 2) + binary.LittleEndian.PutUint16(bytes, value) + return bytes +} + +func (c Encoder) FromUint32(value uint32) []byte { + bytes := make([]byte, 4) + binary.LittleEndian.PutUint32(bytes, value) + return bytes +} + +func (c Encoder) FromUint64(value uint64) []byte { + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, value) + return bytes +} diff --git a/packages/vm/wasmlib/go/wasmclient/results.go b/packages/vm/wasmlib/go/wasmclient/results.go index af3747737e..1af5ff4a2f 100644 --- a/packages/vm/wasmlib/go/wasmclient/results.go +++ b/packages/vm/wasmlib/go/wasmclient/results.go @@ -3,125 +3,22 @@ package wasmclient -import ( - "github.com/iotaledger/wasp/packages/kv" - "github.com/iotaledger/wasp/packages/kv/dict" -) - -// The Results struct is used to gather all arguments for a smart -// contract function call and encode it into a deterministic byte array +// The Results struct contains the results from a smart contract function call type Results struct { - res dict.Dict + Decoder + res ResMap } func (r Results) Exists(key string) bool { - _, ok := r.res[kv.Key(key)] - return ok -} - -func (r Results) ForEach(pair func(key string, val string)) { - r.res.ForEach(func(key kv.Key, value []byte) bool { - pair(Base58Encode([]byte(key)), string(key)) - return true - }) + return r.res.Get(key) != nil } -func (r Results) get(key string, typeID int32) []byte { - size := TypeSizes[typeID] - bytes, ok := r.res[kv.Key(key)] - if ok { - if size != 0 && len(bytes) != int(size) { - panic("invalid type size") - } - return bytes +func (r Results) ForEach(keyValue func(key []byte, val []byte)) { + for key, val := range r.res { + keyValue([]byte(key), val) } - // return default all-zero bytes value - return make([]byte, size) -} - -func (r Results) getBase58(key string, typeID int32) string { - return Base58Encode(r.get(key, typeID)) -} - -func (r Results) GetAddress(key string) Address { - return Address(r.getBase58(key, TYPE_ADDRESS)) -} - -func (r Results) GetAgentID(key string) AgentID { - return AgentID(r.getBase58(key, TYPE_AGENT_ID)) -} - -func (r Results) GetBytes(key string) []byte { - return r.get(key, TYPE_BYTES) } -func (r Results) GetBool(key string) bool { - return r.get(key, TYPE_BOOL)[0] != 0 +func (r Results) Get(key string) []byte { + return r.res.Get(key) } - -func (r Results) GetChainID(key string) ChainID { - return ChainID(r.getBase58(key, TYPE_CHAIN_ID)) -} - -func (r Results) GetColor(key string) Color { - return Color(r.getBase58(key, TYPE_COLOR)) -} - -func (r Results) GetHash(key string) Hash { - return Hash(r.getBase58(key, TYPE_HASH)) -} - -func (r Results) GetHname(key string) Hname { - return Hname(r.getUint64(key, TYPE_HNAME)) -} - -func (r Results) GetInt8(key string) int8 { - return int8(r.get(key, TYPE_INT8)[0]) -} - -func (r Results) GetInt16(key string) int16 { - return int16(r.getUint64(key, TYPE_INT16)) -} - -func (r Results) GetInt32(key string) int32 { - return int32(r.getUint64(key, TYPE_INT32)) -} - -func (r Results) GetInt64(key string) int64 { - return int64(r.getUint64(key, TYPE_INT64)) -} - -func (r Results) GetRequestID(key string) RequestID { - return RequestID(r.getBase58(key, TYPE_REQUEST_ID)) -} - -func (r Results) GetString(key string) string { - return string(r.get(key, TYPE_STRING)) -} - -func (r Results) GetUint8(key string) uint8 { - return r.get(key, TYPE_INT8)[0] -} - -func (r Results) GetUint16(key string) uint16 { - return uint16(r.getUint64(key, TYPE_INT16)) -} - -func (r Results) GetUint32(key string) uint32 { - return uint32(r.getUint64(key, TYPE_INT32)) -} - -func (r Results) GetUint64(key string) uint64 { - return r.getUint64(key, TYPE_INT64) -} - -func (r Results) getUint64(key string, typeID int32) uint64 { - b := r.get(key, typeID) - v := uint64(0) - for i := len(b) - 1; i >= 0; i-- { - v = (v << 8) | uint64(b[i]) - } - return v -} - -// TODO Decode() from view call response into map diff --git a/packages/vm/wasmlib/go/wasmclient/service.go b/packages/vm/wasmlib/go/wasmclient/service.go index e68da2abfe..fbd0a6778b 100644 --- a/packages/vm/wasmlib/go/wasmclient/service.go +++ b/packages/vm/wasmlib/go/wasmclient/service.go @@ -14,11 +14,28 @@ import ( "github.com/iotaledger/wasp/packages/iscp/colored" "github.com/iotaledger/wasp/packages/iscp/request" "github.com/iotaledger/wasp/packages/iscp/requestargs" + "github.com/iotaledger/wasp/packages/kv" "github.com/iotaledger/wasp/packages/kv/dict" "github.com/iotaledger/wasp/packages/subscribe" "github.com/mr-tron/base58" ) +type ArgMap dict.Dict + +func (m ArgMap) Get(key string) []byte { + return m[kv.Key(key)] +} + +func (m ArgMap) Set(key string, value []byte) { + m[kv.Key(key)] = value +} + +type ResMap dict.Dict + +func (m ResMap) Get(key string) []byte { + return m[kv.Key(key)] +} + type Service struct { chainID *iscp.ChainID keyPair *ed25519.KeyPair @@ -48,21 +65,22 @@ func (s *Service) AsClientView() ClientView { return ClientView{svc: s} } -func (s *Service) CallView(viewName string, args *Arguments) (dict.Dict, error) { - if args == nil { - args = &Arguments{} +func (s *Service) CallView(viewName string, args ArgMap) (ResMap, error) { + res, err := s.waspClient.CallView(s.chainID, s.scHname, viewName, dict.Dict(args)) + if err != nil { + return nil, err } - return s.waspClient.CallView(s.chainID, s.scHname, viewName, args.args) + return ResMap(res), nil } -func (s *Service) PostRequest(hFuncName uint32, args *Arguments, transfer *Transfer, keyPair *ed25519.KeyPair, onLedger bool) Request { +func (s *Service) PostRequest(hFuncName uint32, args ArgMap, transfer *Transfer, keyPair *ed25519.KeyPair, onLedger bool) Request { bal, err := makeBalances(transfer) if err != nil { return Request{err: err} } reqArgs := requestargs.New() if args != nil { - reqArgs.AddEncodeSimpleMany(args.args) + reqArgs.AddEncodeSimpleMany(dict.Dict(args)) } if onLedger { diff --git a/packages/vm/wasmlib/go/wasmclient/transfer.go b/packages/vm/wasmlib/go/wasmclient/transfer.go index 58d16f5e4e..45541ddda2 100644 --- a/packages/vm/wasmlib/go/wasmclient/transfer.go +++ b/packages/vm/wasmlib/go/wasmclient/transfer.go @@ -24,8 +24,8 @@ func TransferTokens(color string, amount uint64) *Transfer { } func (t *Transfer) Set(color string, amount uint64) { - if color == "IOTA" { - color = "11111111111111111111111111111111" + if color == COLOR_IOTA { + color = COLOR_IOTA_BASE58 } t.xfer[color] = amount } diff --git a/packages/vm/wasmlib/go/wasmclient/types.go b/packages/vm/wasmlib/go/wasmclient/types.go index 87e618abb9..d71ea560ea 100644 --- a/packages/vm/wasmlib/go/wasmclient/types.go +++ b/packages/vm/wasmlib/go/wasmclient/types.go @@ -20,6 +20,9 @@ const ( TYPE_MAP int32 = 13 TYPE_REQUEST_ID int32 = 14 TYPE_STRING int32 = 15 + + COLOR_IOTA = "IOTA" + COLOR_IOTA_BASE58 = "11111111111111111111111111111111" ) type ( diff --git a/packages/vm/wasmlib/ts/wasmclient/arguments.ts b/packages/vm/wasmlib/ts/wasmclient/arguments.ts index 519648a0b6..b181bde558 100644 --- a/packages/vm/wasmlib/ts/wasmclient/arguments.ts +++ b/packages/vm/wasmlib/ts/wasmclient/arguments.ts @@ -2,145 +2,39 @@ // SPDX-License-Identifier: Apache-2.0 import * as wasmclient from "./index" -import {Base58} from "./crypto"; import {Buffer} from "./buffer"; // The Arguments struct is used to gather all arguments for this smart // contract function call and encode it into this deterministic byte array -export class Arguments { +export class Arguments extends wasmclient.Encoder { private args = new Map(); - get(key: string): wasmclient.Bytes { - const bytes = this.args.get(key); - return bytes ?? Buffer.alloc(0); - } + // get(key: string): wasmclient.Bytes { + // const bytes = this.args.get(key); + // return bytes ?? Buffer.alloc(0); + // } - private set(key: string, val: wasmclient.Bytes): void { + public set(key: string, val: wasmclient.Bytes): void { this.args.set(key, val); } - private setBase58(key: string, val: string, typeID: wasmclient.Int32): void { - const bytes = Base58.decode(val); - if (bytes.length != wasmclient.TYPE_SIZES[typeID]) { - wasmclient.panic("invalid byte size"); - } - this.set(key, bytes); - } - - indexedKey(key: string, index: wasmclient.Int32): string { + public indexedKey(key: string, index: wasmclient.Int32): string { return key + "." + index.toString(); } - mandatory(key: string): void { + public mandatory(key: string): void { if (!this.args.has(key)) { wasmclient.panic("missing mandatory " + key) } } - setAddress(key: string, val: wasmclient.AgentID): void { - this.setBase58(key, val, wasmclient.TYPE_ADDRESS); - } - - setAgentID(key: string, val: wasmclient.AgentID): void { - this.setBase58(key, val, wasmclient.TYPE_AGENT_ID); - } - - setBool(key: string, val: boolean): void { - const bytes = Buffer.alloc(1); - if (val) { - bytes.writeUInt8(1, 0); - } - this.set(key, bytes) - } - - setBytes(key: string, val: wasmclient.Bytes): void { - this.set(key, Buffer.from(val)); - } - - setColor(key: string, val: wasmclient.Color): void { - this.setBase58(key, val, wasmclient.TYPE_COLOR); - } - - setChainID(key: string, val: wasmclient.ChainID): void { - this.setBase58(key, val, wasmclient.TYPE_CHAIN_ID); - } - - setHash(key: string, val: wasmclient.Hash): void { - this.setBase58(key, val, wasmclient.TYPE_HASH); - } - - setHname(key: string, val: wasmclient.Hname): void { - this.setUint32(key, val); - } - - setInt8(key: string, val: wasmclient.Int8): void { - const bytes = Buffer.alloc(1); - bytes.writeInt8(val, 0); - this.set(key, bytes); - } - - setInt16(key: string, val: wasmclient.Int16): void { - const bytes = Buffer.alloc(2); - bytes.writeInt16LE(val, 0); - this.set(key, bytes); - } - - setInt32(key: string, val: wasmclient.Int32): void { - const bytes = Buffer.alloc(4); - bytes.writeInt32LE(val, 0); - this.set(key, bytes); - } - - setInt64(key: string, val: wasmclient.Int64): void { - const bytes = Buffer.alloc(8); - bytes.writeBigInt64LE(val, 0); - this.set(key, bytes); - } - - setRequestID(key: string, val: wasmclient.RequestID): void { - this.setBase58(key, val, wasmclient.TYPE_REQUEST_ID); - } - - getString(key: string): string { - const bytes = this.get(key); - return bytes.toString(); - } - - setString(key: string, val: string): void { - this.set(key, Buffer.from(val)); - } - - setUint8(key: string, val: wasmclient.Uint8): void { - const bytes = Buffer.alloc(1); - bytes.writeUInt8(val, 0); - this.set(key, bytes); - } - - setUint16(key: string, val: wasmclient.Uint16): void { - const bytes = Buffer.alloc(2); - bytes.writeUInt16LE(val, 0); - this.set(key, bytes); - } - - setUint32(key: string, val: wasmclient.Uint32): void { - const bytes = Buffer.alloc(4); - bytes.writeUInt32LE(val, 0); - this.set(key, bytes); - } - - setUint64(key: string, val: wasmclient.Uint64): void { - const bytes = Buffer.alloc(8); - bytes.writeBigUInt64LE(val, 0); - this.set(key, bytes); - } - // Encode returns a byte array that encodes the Arguments as follows: // Sort all keys in ascending order (very important, because this data // will be part of the data that will be signed, so the order needs to // be 100% deterministic). Then emit the 4-byte argument count. // Next for each argument emit the 2-byte key length, the key prepended // with the minus sign, the 4-byte value length, and then the value bytes. - encode(): wasmclient.Bytes { + public encode(): wasmclient.Bytes { const keys = new Array(); for (const key of this.args.keys()) { keys.push(key); @@ -164,7 +58,7 @@ export class Arguments { return buf; } - encodeCall(): wasmclient.Items { + public encodeCall(): wasmclient.Items { let items = new wasmclient.Items() for (const [key, val] of this.args) { const k = Buffer.from(key).toString("base64"); diff --git a/packages/vm/wasmlib/ts/wasmclient/clientview.ts b/packages/vm/wasmlib/ts/wasmclient/clientview.ts index 8fbde5939b..22bf3f74e5 100644 --- a/packages/vm/wasmlib/ts/wasmclient/clientview.ts +++ b/packages/vm/wasmlib/ts/wasmclient/clientview.ts @@ -10,10 +10,10 @@ export class ClientView { this.svc = svc; } - protected async callView(viewName: string, args: wasmclient.Arguments | null): Promise { + protected async callView(viewName: string, args: wasmclient.Arguments | null, res: wasmclient.Results): Promise { if (args == null) { args = new wasmclient.Arguments(); } - return await this.svc.callView(viewName, args); + await this.svc.callView(viewName, args, res); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts index cc25b08aa0..c12b2bb7ed 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts @@ -21,7 +21,7 @@ export class DepositFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public agentID(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgAgentID, v); + this.args.set(ArgAgentID, this.args.fromAgentID(v)); } public async post(): Promise { @@ -35,11 +35,11 @@ export class HarvestFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public withdrawAmount(v: wasmclient.Int64): void { - this.args.setInt64(ArgWithdrawAmount, v); + this.args.set(ArgWithdrawAmount, this.args.fromInt64(v)); } public withdrawColor(v: wasmclient.Color): void { - this.args.setColor(ArgWithdrawColor, v); + this.args.set(ArgWithdrawColor, this.args.fromColor(v)); } public async post(): Promise { @@ -61,16 +61,18 @@ export class WithdrawFunc extends wasmclient.ClientFunc { export class AccountsView extends wasmclient.ClientView { public async call(): Promise { - return new AccountsResults(await this.callView("accounts", null)); + const res = new AccountsResults(); + await this.callView("accounts", null, res); + return res; } } -export class AccountsResults extends wasmclient.ViewResults { +export class AccountsResults extends wasmclient.Results { agents(): Map { - const res = new Map(); - this.res.forEach((key, val) => { - res.set(key, this.res.getBytes(val)); + const res = new Map(); + this.forEach((key, val) => { + res.set(this.toAgentID(key), this.toBytes(val)); }); return res; } @@ -82,21 +84,23 @@ export class BalanceView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public agentID(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgAgentID, v); + this.args.set(ArgAgentID, this.args.fromAgentID(v)); } public async call(): Promise { this.args.mandatory(ArgAgentID); - return new BalanceResults(await this.callView("balance", this.args)); + const res = new BalanceResults(); + await this.callView("balance", this.args, res); + return res; } } -export class BalanceResults extends wasmclient.ViewResults { +export class BalanceResults extends wasmclient.Results { balances(): Map { - const res = new Map(); - this.res.forEach((key, val) => { - res.set(key, this.res.getInt64(val)); + const res = new Map(); + this.forEach((key, val) => { + res.set(this.toColor(key), this.toInt64(val)); }); return res; } @@ -108,19 +112,21 @@ export class GetAccountNonceView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public agentID(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgAgentID, v); + this.args.set(ArgAgentID, this.args.fromAgentID(v)); } public async call(): Promise { this.args.mandatory(ArgAgentID); - return new GetAccountNonceResults(await this.callView("getAccountNonce", this.args)); + const res = new GetAccountNonceResults(); + await this.callView("getAccountNonce", this.args, res); + return res; } } -export class GetAccountNonceResults extends wasmclient.ViewResults { +export class GetAccountNonceResults extends wasmclient.Results { accountNonce(): wasmclient.Int64 { - return this.res.getInt64(ResAccountNonce); + return this.toInt64(this.get(ResAccountNonce)); } } @@ -129,16 +135,18 @@ export class GetAccountNonceResults extends wasmclient.ViewResults { export class TotalAssetsView extends wasmclient.ClientView { public async call(): Promise { - return new TotalAssetsResults(await this.callView("totalAssets", null)); + const res = new TotalAssetsResults(); + await this.callView("totalAssets", null, res); + return res; } } -export class TotalAssetsResults extends wasmclient.ViewResults { +export class TotalAssetsResults extends wasmclient.Results { balances(): Map { - const res = new Map(); - this.res.forEach((key, val) => { - res.set(key, this.res.getInt64(val)); + const res = new Map(); + this.forEach((key, val) => { + res.set(this.toColor(key), this.toInt64(val)); }); return res; } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts index 0302c74563..c79e3d5717 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts @@ -21,7 +21,7 @@ export class StoreBlobFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public blobs(v: wasmclient.Bytes): void { - this.args.setBytes(ArgBlobs, v); + this.args.set(ArgBlobs, this.args.fromBytes(v)); } public async post(): Promise { @@ -29,10 +29,10 @@ export class StoreBlobFunc extends wasmclient.ClientFunc { } } -export class StoreBlobResults extends wasmclient.ViewResults { +export class StoreBlobResults extends wasmclient.Results { hash(): wasmclient.Hash { - return this.res.getHash(ResHash); + return this.toHash(this.get(ResHash)); } } @@ -42,24 +42,26 @@ export class GetBlobFieldView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public field(v: string): void { - this.args.setString(ArgField, v); + this.args.set(ArgField, this.args.fromString(v)); } public hash(v: wasmclient.Hash): void { - this.args.setHash(ArgHash, v); + this.args.set(ArgHash, this.args.fromHash(v)); } public async call(): Promise { this.args.mandatory(ArgField); this.args.mandatory(ArgHash); - return new GetBlobFieldResults(await this.callView("getBlobField", this.args)); + const res = new GetBlobFieldResults(); + await this.callView("getBlobField", this.args, res); + return res; } } -export class GetBlobFieldResults extends wasmclient.ViewResults { +export class GetBlobFieldResults extends wasmclient.Results { bytes(): wasmclient.Bytes { - return this.res.getBytes(ResBytes); + return this.toBytes(this.get(ResBytes)); } } @@ -69,21 +71,23 @@ export class GetBlobInfoView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public hash(v: wasmclient.Hash): void { - this.args.setHash(ArgHash, v); + this.args.set(ArgHash, this.args.fromHash(v)); } public async call(): Promise { this.args.mandatory(ArgHash); - return new GetBlobInfoResults(await this.callView("getBlobInfo", this.args)); + const res = new GetBlobInfoResults(); + await this.callView("getBlobInfo", this.args, res); + return res; } } -export class GetBlobInfoResults extends wasmclient.ViewResults { +export class GetBlobInfoResults extends wasmclient.Results { blobSizes(): Map { - const res = new Map(); - this.res.forEach((key, val) => { - res.set(key, this.res.getInt32(val)); + const res = new Map(); + this.forEach((key, val) => { + res.set(this.toString(key), this.toInt32(val)); }); return res; } @@ -94,16 +98,18 @@ export class GetBlobInfoResults extends wasmclient.ViewResults { export class ListBlobsView extends wasmclient.ClientView { public async call(): Promise { - return new ListBlobsResults(await this.callView("listBlobs", null)); + const res = new ListBlobsResults(); + await this.callView("listBlobs", null, res); + return res; } } -export class ListBlobsResults extends wasmclient.ViewResults { +export class ListBlobsResults extends wasmclient.Results { blobSizes(): Map { - const res = new Map(); - this.res.forEach((key, val) => { - res.set(key, this.res.getInt32(val)); + const res = new Map(); + this.forEach((key, val) => { + res.set(this.toHash(key), this.toInt32(val)); }); return res; } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts index 0705ca882c..4e391244b0 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts @@ -28,22 +28,24 @@ const ResStateControllerAddress = "s"; export class ControlAddressesView extends wasmclient.ClientView { public async call(): Promise { - return new ControlAddressesResults(await this.callView("controlAddresses", null)); + const res = new ControlAddressesResults(); + await this.callView("controlAddresses", null, res); + return res; } } -export class ControlAddressesResults extends wasmclient.ViewResults { +export class ControlAddressesResults extends wasmclient.Results { blockIndex(): wasmclient.Int32 { - return this.res.getInt32(ResBlockIndex); + return this.toInt32(this.get(ResBlockIndex)); } governingAddress(): wasmclient.Address { - return this.res.getAddress(ResGoverningAddress); + return this.toAddress(this.get(ResGoverningAddress)); } stateControllerAddress(): wasmclient.Address { - return this.res.getAddress(ResStateControllerAddress); + return this.toAddress(this.get(ResStateControllerAddress)); } } @@ -53,19 +55,21 @@ export class GetBlockInfoView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public blockIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgBlockIndex, v); + this.args.set(ArgBlockIndex, this.args.fromInt32(v)); } public async call(): Promise { this.args.mandatory(ArgBlockIndex); - return new GetBlockInfoResults(await this.callView("getBlockInfo", this.args)); + const res = new GetBlockInfoResults(); + await this.callView("getBlockInfo", this.args, res); + return res; } } -export class GetBlockInfoResults extends wasmclient.ViewResults { +export class GetBlockInfoResults extends wasmclient.Results { blockInfo(): wasmclient.Bytes { - return this.res.getBytes(ResBlockInfo); + return this.toBytes(this.get(ResBlockInfo)); } } @@ -75,19 +79,21 @@ export class GetEventsForBlockView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public blockIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgBlockIndex, v); + this.args.set(ArgBlockIndex, this.args.fromInt32(v)); } public async call(): Promise { this.args.mandatory(ArgBlockIndex); - return new GetEventsForBlockResults(await this.callView("getEventsForBlock", this.args)); + const res = new GetEventsForBlockResults(); + await this.callView("getEventsForBlock", this.args, res); + return res; } } -export class GetEventsForBlockResults extends wasmclient.ViewResults { +export class GetEventsForBlockResults extends wasmclient.Results { event(): wasmclient.Bytes { - return this.res.getBytes(ResEvent); + return this.toBytes(this.get(ResEvent)); } } @@ -97,27 +103,29 @@ export class GetEventsForContractView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public contractHname(v: wasmclient.Hname): void { - this.args.setHname(ArgContractHname, v); + this.args.set(ArgContractHname, this.args.fromHname(v)); } public fromBlock(v: wasmclient.Int32): void { - this.args.setInt32(ArgFromBlock, v); + this.args.set(ArgFromBlock, this.args.fromInt32(v)); } public toBlock(v: wasmclient.Int32): void { - this.args.setInt32(ArgToBlock, v); + this.args.set(ArgToBlock, this.args.fromInt32(v)); } public async call(): Promise { this.args.mandatory(ArgContractHname); - return new GetEventsForContractResults(await this.callView("getEventsForContract", this.args)); + const res = new GetEventsForContractResults(); + await this.callView("getEventsForContract", this.args, res); + return res; } } -export class GetEventsForContractResults extends wasmclient.ViewResults { +export class GetEventsForContractResults extends wasmclient.Results { event(): wasmclient.Bytes { - return this.res.getBytes(ResEvent); + return this.toBytes(this.get(ResEvent)); } } @@ -127,19 +135,21 @@ export class GetEventsForRequestView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public requestID(v: wasmclient.RequestID): void { - this.args.setRequestID(ArgRequestID, v); + this.args.set(ArgRequestID, this.args.fromRequestID(v)); } public async call(): Promise { this.args.mandatory(ArgRequestID); - return new GetEventsForRequestResults(await this.callView("getEventsForRequest", this.args)); + const res = new GetEventsForRequestResults(); + await this.callView("getEventsForRequest", this.args, res); + return res; } } -export class GetEventsForRequestResults extends wasmclient.ViewResults { +export class GetEventsForRequestResults extends wasmclient.Results { event(): wasmclient.Bytes { - return this.res.getBytes(ResEvent); + return this.toBytes(this.get(ResEvent)); } } @@ -148,18 +158,20 @@ export class GetEventsForRequestResults extends wasmclient.ViewResults { export class GetLatestBlockInfoView extends wasmclient.ClientView { public async call(): Promise { - return new GetLatestBlockInfoResults(await this.callView("getLatestBlockInfo", null)); + const res = new GetLatestBlockInfoResults(); + await this.callView("getLatestBlockInfo", null, res); + return res; } } -export class GetLatestBlockInfoResults extends wasmclient.ViewResults { +export class GetLatestBlockInfoResults extends wasmclient.Results { blockIndex(): wasmclient.Int32 { - return this.res.getInt32(ResBlockIndex); + return this.toInt32(this.get(ResBlockIndex)); } blockInfo(): wasmclient.Bytes { - return this.res.getBytes(ResBlockInfo); + return this.toBytes(this.get(ResBlockInfo)); } } @@ -169,19 +181,21 @@ export class GetRequestIDsForBlockView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public blockIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgBlockIndex, v); + this.args.set(ArgBlockIndex, this.args.fromInt32(v)); } public async call(): Promise { this.args.mandatory(ArgBlockIndex); - return new GetRequestIDsForBlockResults(await this.callView("getRequestIDsForBlock", this.args)); + const res = new GetRequestIDsForBlockResults(); + await this.callView("getRequestIDsForBlock", this.args, res); + return res; } } -export class GetRequestIDsForBlockResults extends wasmclient.ViewResults { +export class GetRequestIDsForBlockResults extends wasmclient.Results { requestID(): wasmclient.RequestID { - return this.res.getRequestID(ResRequestID); + return this.toRequestID(this.get(ResRequestID)); } } @@ -191,27 +205,29 @@ export class GetRequestReceiptView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public requestID(v: wasmclient.RequestID): void { - this.args.setRequestID(ArgRequestID, v); + this.args.set(ArgRequestID, this.args.fromRequestID(v)); } public async call(): Promise { this.args.mandatory(ArgRequestID); - return new GetRequestReceiptResults(await this.callView("getRequestReceipt", this.args)); + const res = new GetRequestReceiptResults(); + await this.callView("getRequestReceipt", this.args, res); + return res; } } -export class GetRequestReceiptResults extends wasmclient.ViewResults { +export class GetRequestReceiptResults extends wasmclient.Results { blockIndex(): wasmclient.Int32 { - return this.res.getInt32(ResBlockIndex); + return this.toInt32(this.get(ResBlockIndex)); } requestIndex(): wasmclient.Int16 { - return this.res.getInt16(ResRequestIndex); + return this.toInt16(this.get(ResRequestIndex)); } requestRecord(): wasmclient.Bytes { - return this.res.getBytes(ResRequestRecord); + return this.toBytes(this.get(ResRequestRecord)); } } @@ -221,19 +237,21 @@ export class GetRequestReceiptsForBlockView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public blockIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgBlockIndex, v); + this.args.set(ArgBlockIndex, this.args.fromInt32(v)); } public async call(): Promise { this.args.mandatory(ArgBlockIndex); - return new GetRequestReceiptsForBlockResults(await this.callView("getRequestReceiptsForBlock", this.args)); + const res = new GetRequestReceiptsForBlockResults(); + await this.callView("getRequestReceiptsForBlock", this.args, res); + return res; } } -export class GetRequestReceiptsForBlockResults extends wasmclient.ViewResults { +export class GetRequestReceiptsForBlockResults extends wasmclient.Results { requestRecord(): wasmclient.Bytes { - return this.res.getBytes(ResRequestRecord); + return this.toBytes(this.get(ResRequestRecord)); } } @@ -243,19 +261,21 @@ export class IsRequestProcessedView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public requestID(v: wasmclient.RequestID): void { - this.args.setRequestID(ArgRequestID, v); + this.args.set(ArgRequestID, this.args.fromRequestID(v)); } public async call(): Promise { this.args.mandatory(ArgRequestID); - return new IsRequestProcessedResults(await this.callView("isRequestProcessed", this.args)); + const res = new IsRequestProcessedResults(); + await this.callView("isRequestProcessed", this.args, res); + return res; } } -export class IsRequestProcessedResults extends wasmclient.ViewResults { +export class IsRequestProcessedResults extends wasmclient.Results { requestProcessed(): string { - return this.res.getString(ResRequestProcessed); + return this.toString(this.get(ResRequestProcessed)); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts b/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts index c37ec7c033..1ff7e42263 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts @@ -36,15 +36,15 @@ export class AddAllowedStateControllerAddressFunc extends wasmclient.ClientFunc private args: wasmclient.Arguments = new wasmclient.Arguments(); public chainOwner(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgChainOwner, v); + this.args.set(ArgChainOwner, this.args.fromAgentID(v)); } public feeColor(v: wasmclient.Color): void { - this.args.setColor(ArgFeeColor, v); + this.args.set(ArgFeeColor, this.args.fromColor(v)); } public stateControllerAddress(v: wasmclient.Address): void { - this.args.setAddress(ArgStateControllerAddress, v); + this.args.set(ArgStateControllerAddress, this.args.fromAddress(v)); } public async post(): Promise { @@ -69,7 +69,7 @@ export class DelegateChainOwnershipFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public chainOwner(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgChainOwner, v); + this.args.set(ArgChainOwner, this.args.fromAgentID(v)); } public async post(): Promise { @@ -84,7 +84,7 @@ export class RemoveAllowedStateControllerAddressFunc extends wasmclient.ClientFu private args: wasmclient.Arguments = new wasmclient.Arguments(); public stateControllerAddress(v: wasmclient.Address): void { - this.args.setAddress(ArgStateControllerAddress, v); + this.args.set(ArgStateControllerAddress, this.args.fromAddress(v)); } public async post(): Promise { @@ -99,7 +99,7 @@ export class RotateStateControllerFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public stateControllerAddress(v: wasmclient.Address): void { - this.args.setAddress(ArgStateControllerAddress, v); + this.args.set(ArgStateControllerAddress, this.args.fromAddress(v)); } public async post(): Promise { @@ -114,23 +114,23 @@ export class SetChainInfoFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public maxBlobSize(v: wasmclient.Int32): void { - this.args.setInt32(ArgMaxBlobSize, v); + this.args.set(ArgMaxBlobSize, this.args.fromInt32(v)); } public maxEventSize(v: wasmclient.Int16): void { - this.args.setInt16(ArgMaxEventSize, v); + this.args.set(ArgMaxEventSize, this.args.fromInt16(v)); } public maxEventsPerReq(v: wasmclient.Int16): void { - this.args.setInt16(ArgMaxEventsPerReq, v); + this.args.set(ArgMaxEventsPerReq, this.args.fromInt16(v)); } public ownerFee(v: wasmclient.Int64): void { - this.args.setInt64(ArgOwnerFee, v); + this.args.set(ArgOwnerFee, this.args.fromInt64(v)); } public validatorFee(v: wasmclient.Int64): void { - this.args.setInt64(ArgValidatorFee, v); + this.args.set(ArgValidatorFee, this.args.fromInt64(v)); } public async post(): Promise { @@ -144,15 +144,15 @@ export class SetContractFeeFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public hname(v: wasmclient.Hname): void { - this.args.setHname(ArgHname, v); + this.args.set(ArgHname, this.args.fromHname(v)); } public ownerFee(v: wasmclient.Int64): void { - this.args.setInt64(ArgOwnerFee, v); + this.args.set(ArgOwnerFee, this.args.fromInt64(v)); } public validatorFee(v: wasmclient.Int64): void { - this.args.setInt64(ArgValidatorFee, v); + this.args.set(ArgValidatorFee, this.args.fromInt64(v)); } public async post(): Promise { @@ -167,11 +167,11 @@ export class SetDefaultFeeFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public ownerFee(v: wasmclient.Int64): void { - this.args.setInt64(ArgOwnerFee, v); + this.args.set(ArgOwnerFee, this.args.fromInt64(v)); } public validatorFee(v: wasmclient.Int64): void { - this.args.setInt64(ArgValidatorFee, v); + this.args.set(ArgValidatorFee, this.args.fromInt64(v)); } public async post(): Promise { @@ -184,14 +184,16 @@ export class SetDefaultFeeFunc extends wasmclient.ClientFunc { export class GetAllowedStateControllerAddressesView extends wasmclient.ClientView { public async call(): Promise { - return new GetAllowedStateControllerAddressesResults(await this.callView("getAllowedStateControllerAddresses", null)); + const res = new GetAllowedStateControllerAddressesResults(); + await this.callView("getAllowedStateControllerAddresses", null, res); + return res; } } -export class GetAllowedStateControllerAddressesResults extends wasmclient.ViewResults { +export class GetAllowedStateControllerAddressesResults extends wasmclient.Results { allowedStateControllerAddresses(): wasmclient.Bytes { - return this.res.getBytes(ResAllowedStateControllerAddresses); + return this.toBytes(this.get(ResAllowedStateControllerAddresses)); } } @@ -200,46 +202,48 @@ export class GetAllowedStateControllerAddressesResults extends wasmclient.ViewRe export class GetChainInfoView extends wasmclient.ClientView { public async call(): Promise { - return new GetChainInfoResults(await this.callView("getChainInfo", null)); + const res = new GetChainInfoResults(); + await this.callView("getChainInfo", null, res); + return res; } } -export class GetChainInfoResults extends wasmclient.ViewResults { +export class GetChainInfoResults extends wasmclient.Results { chainID(): wasmclient.ChainID { - return this.res.getChainID(ResChainID); + return this.toChainID(this.get(ResChainID)); } chainOwnerID(): wasmclient.AgentID { - return this.res.getAgentID(ResChainOwnerID); + return this.toAgentID(this.get(ResChainOwnerID)); } defaultOwnerFee(): wasmclient.Int64 { - return this.res.getInt64(ResDefaultOwnerFee); + return this.toInt64(this.get(ResDefaultOwnerFee)); } defaultValidatorFee(): wasmclient.Int64 { - return this.res.getInt64(ResDefaultValidatorFee); + return this.toInt64(this.get(ResDefaultValidatorFee)); } description(): string { - return this.res.getString(ResDescription); + return this.toString(this.get(ResDescription)); } feeColor(): wasmclient.Color { - return this.res.getColor(ResFeeColor); + return this.toColor(this.get(ResFeeColor)); } maxBlobSize(): wasmclient.Int32 { - return this.res.getInt32(ResMaxBlobSize); + return this.toInt32(this.get(ResMaxBlobSize)); } maxEventSize(): wasmclient.Int16 { - return this.res.getInt16(ResMaxEventSize); + return this.toInt16(this.get(ResMaxEventSize)); } maxEventsPerReq(): wasmclient.Int16 { - return this.res.getInt16(ResMaxEventsPerReq); + return this.toInt16(this.get(ResMaxEventsPerReq)); } } @@ -249,27 +253,29 @@ export class GetFeeInfoView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public hname(v: wasmclient.Hname): void { - this.args.setHname(ArgHname, v); + this.args.set(ArgHname, this.args.fromHname(v)); } public async call(): Promise { this.args.mandatory(ArgHname); - return new GetFeeInfoResults(await this.callView("getFeeInfo", this.args)); + const res = new GetFeeInfoResults(); + await this.callView("getFeeInfo", this.args, res); + return res; } } -export class GetFeeInfoResults extends wasmclient.ViewResults { +export class GetFeeInfoResults extends wasmclient.Results { feeColor(): wasmclient.Color { - return this.res.getColor(ResFeeColor); + return this.toColor(this.get(ResFeeColor)); } ownerFee(): wasmclient.Int64 { - return this.res.getInt64(ResOwnerFee); + return this.toInt64(this.get(ResOwnerFee)); } validatorFee(): wasmclient.Int64 { - return this.res.getInt64(ResValidatorFee); + return this.toInt64(this.get(ResValidatorFee)); } } @@ -278,14 +284,16 @@ export class GetFeeInfoResults extends wasmclient.ViewResults { export class GetMaxBlobSizeView extends wasmclient.ClientView { public async call(): Promise { - return new GetMaxBlobSizeResults(await this.callView("getMaxBlobSize", null)); + const res = new GetMaxBlobSizeResults(); + await this.callView("getMaxBlobSize", null, res); + return res; } } -export class GetMaxBlobSizeResults extends wasmclient.ViewResults { +export class GetMaxBlobSizeResults extends wasmclient.Results { maxBlobSize(): wasmclient.Int32 { - return this.res.getInt32(ResMaxBlobSize); + return this.toInt32(this.get(ResMaxBlobSize)); } } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts index 2009f5dfd4..f661a112c9 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts @@ -23,15 +23,15 @@ export class DeployContractFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public description(v: string): void { - this.args.setString(ArgDescription, v); + this.args.set(ArgDescription, this.args.fromString(v)); } public name(v: string): void { - this.args.setString(ArgName, v); + this.args.set(ArgName, this.args.fromString(v)); } public programHash(v: wasmclient.Hash): void { - this.args.setHash(ArgProgramHash, v); + this.args.set(ArgProgramHash, this.args.fromHash(v)); } public async post(): Promise { @@ -47,7 +47,7 @@ export class GrantDeployPermissionFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public deployer(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgDeployer, v); + this.args.set(ArgDeployer, this.args.fromAgentID(v)); } public async post(): Promise { @@ -62,7 +62,7 @@ export class RevokeDeployPermissionFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public deployer(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgDeployer, v); + this.args.set(ArgDeployer, this.args.fromAgentID(v)); } public async post(): Promise { @@ -77,23 +77,25 @@ export class FindContractView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public hname(v: wasmclient.Hname): void { - this.args.setHname(ArgHname, v); + this.args.set(ArgHname, this.args.fromHname(v)); } public async call(): Promise { this.args.mandatory(ArgHname); - return new FindContractResults(await this.callView("findContract", this.args)); + const res = new FindContractResults(); + await this.callView("findContract", this.args, res); + return res; } } -export class FindContractResults extends wasmclient.ViewResults { +export class FindContractResults extends wasmclient.Results { contractFound(): wasmclient.Bytes { - return this.res.getBytes(ResContractFound); + return this.toBytes(this.get(ResContractFound)); } contractRecData(): wasmclient.Bytes { - return this.res.getBytes(ResContractRecData); + return this.toBytes(this.get(ResContractRecData)); } } @@ -102,16 +104,18 @@ export class FindContractResults extends wasmclient.ViewResults { export class GetContractRecordsView extends wasmclient.ClientView { public async call(): Promise { - return new GetContractRecordsResults(await this.callView("getContractRecords", null)); + const res = new GetContractRecordsResults(); + await this.callView("getContractRecords", null, res); + return res; } } -export class GetContractRecordsResults extends wasmclient.ViewResults { +export class GetContractRecordsResults extends wasmclient.Results { contractRegistry(): Map { - const res = new Map(); - this.res.forEach((key, val) => { - res.set(key, this.res.getBytes(val)); + const res = new Map(); + this.forEach((key, val) => { + res.set(this.toHname(key), this.toBytes(val)); }); return res; } diff --git a/packages/vm/wasmlib/ts/wasmclient/decoder.ts b/packages/vm/wasmlib/ts/wasmclient/decoder.ts new file mode 100644 index 0000000000..c83f5a9e64 --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/decoder.ts @@ -0,0 +1,98 @@ +// The Results struct is used to gather all arguments for a smart +// contract function call and encode it into a deterministic byte array +import * as wasmclient from "./index"; +import {Base58} from "./crypto"; +import {Buffer} from "./buffer"; + +export class Decoder { + private static checkDefault(bytes: Buffer | undefined, typeID: wasmclient.Int32): Buffer { + const size = wasmclient.TYPE_SIZES[typeID]; + if (bytes === undefined) { + return Buffer.alloc(size); + } + if (size != 0 && bytes.length != size) { + wasmclient.panic("invalid type size"); + } + return bytes; + } + + private static toBase58(bytes: Buffer | undefined, typeID: wasmclient.Int32): string { + return Base58.encode(Decoder.checkDefault(bytes, typeID)); + } + + protected toAddress(bytes: Buffer | undefined): wasmclient.Address { + return Decoder.toBase58(bytes, wasmclient.TYPE_ADDRESS); + } + + protected toAgentID(bytes: Buffer | undefined): wasmclient.AgentID { + return Decoder.toBase58(bytes, wasmclient.TYPE_AGENT_ID); + } + + protected toBytes(bytes: Buffer | undefined): wasmclient.Bytes { + return Decoder.checkDefault(bytes, wasmclient.TYPE_BYTES) + } + + protected toBool(bytes: Buffer | undefined): wasmclient.Bool { + return Decoder.checkDefault(bytes, wasmclient.TYPE_BOOL)[0] != 0; + } + + protected toChainID(bytes: Buffer | undefined): wasmclient.ChainID { + return Decoder.toBase58(bytes, wasmclient.TYPE_CHAIN_ID); + } + + protected toColor(bytes: Buffer | undefined): wasmclient.Color { + let color = Decoder.toBase58(bytes, wasmclient.TYPE_COLOR); + if (color == "11111111111111111111111111111111") { + color = "IOTA"; + } + return color; + } + + protected toHash(bytes: Buffer | undefined): wasmclient.Hash { + return Decoder.toBase58(bytes, wasmclient.TYPE_HASH); + } + + protected toHname(bytes: Buffer | undefined): wasmclient.Hname { + return Decoder.checkDefault(bytes, wasmclient.TYPE_HNAME).readUInt32LE(0); + } + + protected toInt8(bytes: Buffer | undefined): wasmclient.Int8 { + return Decoder.checkDefault(bytes, wasmclient.TYPE_INT8).readInt8(0); + } + + protected toInt16(bytes: Buffer | undefined): wasmclient.Int16 { + return Decoder.checkDefault(bytes, wasmclient.TYPE_INT16).readInt16LE(0); + } + + protected toInt32(bytes: Buffer | undefined): wasmclient.Int32 { + return Decoder.checkDefault(bytes, wasmclient.TYPE_INT32).readInt32LE(0); + } + + protected toInt64(bytes: Buffer | undefined): wasmclient.Int64 { + return Decoder.checkDefault(bytes, wasmclient.TYPE_INT64).readBigInt64LE(0); + } + + protected toRequestID(bytes: Buffer | undefined): wasmclient.RequestID { + return Decoder.toBase58(bytes, wasmclient.TYPE_REQUEST_ID); + } + + protected toString(bytes: Buffer | undefined): wasmclient.String { + return Decoder.checkDefault(bytes, wasmclient.TYPE_STRING).toString(); + } + + protected toUint8(bytes: Buffer | undefined): wasmclient.Uint8 { + return Decoder.checkDefault(bytes, wasmclient.TYPE_INT8).readUInt8(0); + } + + protected toUint16(bytes: Buffer | undefined): wasmclient.Uint16 { + return Decoder.checkDefault(bytes, wasmclient.TYPE_INT16).readUInt16LE(0); + } + + protected toUint32(bytes: Buffer | undefined): wasmclient.Uint32 { + return Decoder.checkDefault(bytes, wasmclient.TYPE_INT32).readUInt32LE(0); + } + + protected toUint64(bytes: Buffer | undefined): wasmclient.Uint64 { + return Decoder.checkDefault(bytes, wasmclient.TYPE_INT64).readBigUInt64LE(0); + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/encoder.ts b/packages/vm/wasmlib/ts/wasmclient/encoder.ts new file mode 100644 index 0000000000..8fba1473fe --- /dev/null +++ b/packages/vm/wasmlib/ts/wasmclient/encoder.ts @@ -0,0 +1,113 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import * as wasmclient from "./index" +import {Base58} from "./crypto"; +import {Buffer} from "./buffer"; + +// The Arguments struct is used to gather all arguments for this smart +// contract function call and encode it into this deterministic byte array +export class Encoder { + private fromBase58(val: string, typeID: wasmclient.Int32): Buffer { + const bytes = Base58.decode(val); + if (bytes.length != wasmclient.TYPE_SIZES[typeID]) { + wasmclient.panic("invalid byte size"); + } + return bytes; + } + + fromAddress(val: wasmclient.AgentID): Buffer { + return this.fromBase58(val, wasmclient.TYPE_ADDRESS); + } + + fromAgentID(val: wasmclient.AgentID): Buffer { + return this.fromBase58(val, wasmclient.TYPE_AGENT_ID); + } + + fromBool(val: boolean): Buffer { + const bytes = Buffer.alloc(1); + if (val) { + bytes.writeUInt8(1, 0); + } + return bytes; + } + + fromBytes(val: wasmclient.Bytes): Buffer { + return val; + } + + fromChainID(val: wasmclient.ChainID): Buffer { + return this.fromBase58(val, wasmclient.TYPE_CHAIN_ID); + } + + fromColor(val: wasmclient.Color): Buffer { + if (val == "IOTA") { + val = "11111111111111111111111111111111"; + } + return this.fromBase58(val, wasmclient.TYPE_COLOR); + } + + fromHash(val: wasmclient.Hash): Buffer { + return this.fromBase58(val, wasmclient.TYPE_HASH); + } + + fromHname(val: wasmclient.Hname): Buffer { + return this.fromUint32(val); + } + + fromInt8(val: wasmclient.Int8): Buffer { + const bytes = Buffer.alloc(1); + bytes.writeInt8(val, 0); + return bytes; + } + + fromInt16(val: wasmclient.Int16): Buffer { + const bytes = Buffer.alloc(2); + bytes.writeInt16LE(val, 0); + return bytes; + } + + fromInt32(val: wasmclient.Int32): Buffer { + const bytes = Buffer.alloc(4); + bytes.writeInt32LE(val, 0); + return bytes; + } + + fromInt64(val: wasmclient.Int64): Buffer { + const bytes = Buffer.alloc(8); + bytes.writeBigInt64LE(val, 0); + return bytes; + } + + fromRequestID(val: wasmclient.RequestID): Buffer { + return this.fromBase58(val, wasmclient.TYPE_REQUEST_ID); + } + + fromString(val: string): Buffer { + return Buffer.from(val); + } + + fromUint8(val: wasmclient.Uint8): Buffer { + const bytes = Buffer.alloc(1); + bytes.writeUInt8(val, 0); + return bytes; + } + + fromUint16(val: wasmclient.Uint16): Buffer { + const bytes = Buffer.alloc(2); + bytes.writeUInt16LE(val, 0); + return bytes; + } + + fromUint32(val: wasmclient.Uint32): Buffer { + const bytes = Buffer.alloc(4); + bytes.writeUInt32LE(val, 0); + return bytes; + } + + fromUint64(val: wasmclient.Uint64): Buffer { + const bytes = Buffer.alloc(8); + bytes.writeBigUInt64LE(val, 0); + return bytes; + } +} diff --git a/packages/vm/wasmlib/ts/wasmclient/index.ts b/packages/vm/wasmlib/ts/wasmclient/index.ts index a336ab8fcf..d09441a314 100644 --- a/packages/vm/wasmlib/ts/wasmclient/index.ts +++ b/packages/vm/wasmlib/ts/wasmclient/index.ts @@ -4,6 +4,8 @@ export * from "./arguments"; export * from "./clientfunc"; export * from "./clientview"; +export * from "./decoder"; +export * from "./encoder"; export * from "./event"; export * from "./results"; export * from "./service"; diff --git a/packages/vm/wasmlib/ts/wasmclient/results.ts b/packages/vm/wasmlib/ts/wasmclient/results.ts index d55a9527bb..0472c92112 100644 --- a/packages/vm/wasmlib/ts/wasmclient/results.ts +++ b/packages/vm/wasmlib/ts/wasmclient/results.ts @@ -1,119 +1,34 @@ // The Results struct is used to gather all arguments for a smart // contract function call and encode it into a deterministic byte array import * as wasmclient from "./index"; -import {Base58} from "./crypto"; import {Buffer} from "./buffer"; -export class ViewResults { - res: wasmclient.Results; +export class Results extends wasmclient.Decoder { + protected keys = new Map(); + protected res = new Map(); - constructor(res: wasmclient.Results) { - this.res = res; - } -} - -export class Results { - res = new Map(); - keys = new Map(); - - exists(key: string): wasmclient.Bool { + protected exists(key: string): boolean { return this.res.has(key); } - forEach(callbackfn: (base58Key: string, valueKey: string) => void): void { - this.keys.forEach((val, key) => { - callbackfn(Base58.encode(val), key); - }) - } - - private get(key: string, typeID: wasmclient.Int32): wasmclient.Bytes { - const size = wasmclient.TYPE_SIZES[typeID]; - const bytes = this.res.get(key); - if (bytes !== undefined) { - if (size != 0 && bytes.length != size) { - wasmclient.panic("invalid type size"); + protected forEach(keyValue: (key: Buffer, val: Buffer) => void): void { + this.res.forEach((val, key) => { + let keyBuf = this.keys.get(key); + if (keyBuf === undefined) { + wasmclient.panic("missing key"); + return; } - return bytes; - } - // return default all-zero bytes value - return Buffer.alloc(size); - } - - private getBase58(key: string, typeID: wasmclient.Int32): string { - return Base58.encode(this.get(key, typeID)); - } - - getAddress(key: string): wasmclient.Address { - return this.getBase58(key, wasmclient.TYPE_ADDRESS); - } - - getAgentID(key: string): wasmclient.AgentID { - return this.getBase58(key, wasmclient.TYPE_AGENT_ID); - } - - getBytes(key: string): wasmclient.Bytes { - return this.get(key, wasmclient.TYPE_BYTES) - } - - getBool(key: string): wasmclient.Bool { - return this.get(key, wasmclient.TYPE_BOOL)[0] != 0; - } - - getChainID(key: string): wasmclient.ChainID { - return this.getBase58(key, wasmclient.TYPE_CHAIN_ID); - } - - getColor(key: string): wasmclient.Color { - return this.getBase58(key, wasmclient.TYPE_COLOR); - } - - getHash(key: string): wasmclient.Hash { - return this.getBase58(key, wasmclient.TYPE_HASH); - } - - getHname(key: string): wasmclient.Hname { - return this.get(key, wasmclient.TYPE_HNAME).readUInt32LE(0); - } - - getInt8(key: string): wasmclient.Int8 { - return this.get(key, wasmclient.TYPE_INT8).readInt8(0); - } - - getInt16(key: string): wasmclient.Int16 { - return this.get(key, wasmclient.TYPE_INT16).readInt16LE(0); - } - - getInt32(key: string): wasmclient.Int32 { - return this.get(key, wasmclient.TYPE_INT32).readInt32LE(0); - } - - getInt64(key: string): wasmclient.Int64 { - return this.get(key, wasmclient.TYPE_INT64).readBigInt64LE(0); - } - - getRequestID(key: string): wasmclient.RequestID { - return this.getBase58(key, wasmclient.TYPE_REQUEST_ID); - } - - getString(key: string): wasmclient.String { - return this.get(key, wasmclient.TYPE_STRING).toString(); - } - - getUint8(key: string): wasmclient.Uint8 { - return this.get(key, wasmclient.TYPE_INT8).readUInt8(0); - } - - getUint16(key: string): wasmclient.Uint16 { - return this.get(key, wasmclient.TYPE_INT16).readUInt16LE(0); + keyValue(keyBuf, val); + }) } - getUint32(key: string): wasmclient.Uint32 { - return this.get(key, wasmclient.TYPE_INT32).readUInt32LE(0); + protected get(key: string): Buffer | undefined { + return this.res.get(key); } - getUint64(key: string): wasmclient.Uint64 { - return this.get(key, wasmclient.TYPE_INT64).readBigUInt64LE(0); + public set(key: Buffer, val: Buffer) { + const stringKey = key.toString(); + this.keys.set(stringKey, key); + this.res.set(stringKey, val); } - - // TODO Decode() from view call response into map } diff --git a/packages/vm/wasmlib/ts/wasmclient/service.ts b/packages/vm/wasmlib/ts/wasmclient/service.ts index 28a5fb7a5b..efb029f008 100644 --- a/packages/vm/wasmlib/ts/wasmclient/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/service.ts @@ -25,12 +25,13 @@ export class Service { } } - public async callView(viewName: string, args: wasmclient.Arguments): Promise { - return await this.serviceClient.waspClient.callView( + public async callView(viewName: string, args: wasmclient.Arguments, res: wasmclient.Results): Promise { + await this.serviceClient.waspClient.callView( this.serviceClient.configuration.chainId, this.scHname.toString(16), viewName, - args.encodeCall() + args.encodeCall(), + res ); } diff --git a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts index 33d2633529..0e34996400 100644 --- a/packages/vm/wasmlib/ts/wasmclient/waspclient.ts +++ b/packages/vm/wasmlib/ts/wasmclient/waspclient.ts @@ -37,25 +37,20 @@ export class WaspClient { else this.waspAPI = "http://" + waspAPI; } - public async callView(chainID: string, contractHName: string, entryPoint: string, args: Items): Promise { + public async callView(chainID: string, contractHName: string, entryPoint: string, args: Items, res: wasmclient.Results): Promise { const result = await requestSender.sendRequestExt( this.waspAPI, "post", `/chain/${chainID}/contract/${contractHName}/callview/${entryPoint}`, args ); - const res = new wasmclient.Results(); - if (result?.body !== null && result.body.Items) { for (const item of result.body.Items) { const key = Buffer.from(item.Key, "base64"); const val = Buffer.from(item.Value, "base64"); - const stringKey = key.toString() - res.res.set(stringKey, val); - res.keys.set(stringKey, key); + res.set(key, val); } } - return res; } public async postRequest(chainID: string, offLedgerRequest: Buffer): Promise { diff --git a/tools/schema/generator/goclienttemplates/service.go b/tools/schema/generator/goclienttemplates/service.go index be94d5e9a7..9dc96e37d6 100644 --- a/tools/schema/generator/goclienttemplates/service.go +++ b/tools/schema/generator/goclienttemplates/service.go @@ -63,7 +63,7 @@ $#if array funcArgSetterArray funcArgSetterBasic "funcArgSetterBasic": ` func (f *$FuncName$Kind) $FldName(v $fldLangType) { - f.args.Set$FldType(Arg$FldName, v) + f.args.Set(Arg$FldName, f.args.From$FldType(v)) } `, // ******************************* @@ -71,9 +71,9 @@ func (f *$FuncName$Kind) $FldName(v $fldLangType) { func (f *$FuncName$Kind) $FldName(a []$fldLangType) { for i, v := range a { - f.args.Set$FldType(f.args.IndexedKey(Arg$FldName, i), v) + f.args.Set(f.args.IndexedKey(Arg$FldName, i), f.args.From$FldType(v)) } - f.args.SetInt32(Arg$FldName, int32(len(a))) + f.args.Set(Arg$FldName, f.args.SetInt32(int32(len(a)))) } `, // ******************************* @@ -125,8 +125,8 @@ $#if map callResultGetterMap callResultGetterBasic func (r *$FuncName$+Results) $FldName() map[$fldKeyLangType]$fldLangType { res := make(map[$fldKeyLangType]$fldLangType) - r.res.ForEach(func(key string, val string) { - res[$fldKeyLangType(key)] = r.res.Get$FldType(val) + r.res.ForEach(func(key []byte, val []byte) { + res[r.res.To$fldMapKey(key)] = r.res.To$FldType(val) }) return res } @@ -136,7 +136,7 @@ func (r *$FuncName$+Results) $FldName() map[$fldKeyLangType]$fldLangType { $#if mandatory else callResultOptional func (r *$FuncName$+Results) $FldName() $fldLangType { - return r.res.Get$FldType(Res$FldName) + return r.res.To$FldType(r.res.Get(Res$FldName)) } `, // ******************************* diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index edb333b7c9..9ec9ca2b22 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -59,7 +59,7 @@ $#if array funcArgSetterArray funcArgSetterBasic "funcArgSetterBasic": ` public $fldName(v: $fldLangType): void { - this.args.set$FldType(Arg$FldName, v); + this.args.set(Arg$FldName, this.args.from$FldType(v)); } `, // ******************************* @@ -67,9 +67,9 @@ $#if array funcArgSetterArray funcArgSetterBasic public $fldName(a: $fldLangType[]): void { for (let i = 0; i < a.length; i++) { - this.args.set$FldType(this.args.indexedKey(Arg$FldName, i), a[i]); + this.args.set(this.args.indexedKey(Arg$FldName, i), this.args.from$FldType(a[i])); } - this.args.setInt32(Arg$FldName, a.length); + this.args.set(Arg$FldName, this.args.setInt32(a.length)); } `, // ******************************* @@ -87,7 +87,9 @@ $#if param execWithArgs execNoArgs public async call(): Promise<$FuncName$+Results> { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs - return new $FuncName$+Results(await this.callView("$funcName", $args)); + const res = new $FuncName$+Results(); + await this.callView("$funcName", $args, res); + return res; } `, // ******************************* @@ -105,7 +107,7 @@ $#set args null // ******************************* "resultStruct": ` -export class $FuncName$+Results extends wasmclient.ViewResults { +export class $FuncName$+Results extends wasmclient.Results { $#each result callResultGetter } `, @@ -117,9 +119,9 @@ $#if map callResultGetterMap callResultGetterBasic "callResultGetterMap": ` $fldName(): Map<$fldKeyLangType, $fldLangType> { - const res = new Map(); - this.res.forEach((key, val) => { - res.set(key, this.res.get$FldType(val)); + const res = new Map<$fldKeyLangType, $fldLangType>(); + this.forEach((key, val) => { + res.set(this.to$fldMapKey(key), this.to$FldType(val)); }); return res; } @@ -129,14 +131,14 @@ $#if map callResultGetterMap callResultGetterBasic $#if mandatory else callResultOptional $fldName(): $fldLangType { - return this.res.get$FldType(Res$FldName); + return this.to$FldType(this.get(Res$FldName)); } `, // ******************************* "callResultOptional": ` $fldName$+Exists(): boolean { - return this.res.exists(Res$FldName) + return this.exists(Res$FldName) } `, // ******************************* From 3f04026c03fd0c71af6724769e1ff5b7e91eb30d Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Mon, 10 Jan 2022 17:16:15 -0800 Subject: [PATCH 092/120] Disable client code tests for now --- .../test/testwasmlib_client_test.go | 2 +- .../ts/testwasmlibclient/service.ts | 112 ++++++++++-------- 2 files changed, 63 insertions(+), 51 deletions(-) diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go index 24420f6964..daff531971 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go @@ -21,7 +21,7 @@ const ( func setupClient(t *testing.T) *testwasmlibclient.TestWasmLibService { // for now skip client tests - // t.SkipNow() + t.SkipNow() require.True(t, wasmclient.SeedIsValid(mySeed)) require.True(t, wasmclient.ChainIsValid(myChainID)) diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts index 1446631506..6a144af5c3 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -46,7 +46,7 @@ export class ArrayClearFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public name(v: string): void { - this.args.setString(ArgName, v); + this.args.set(ArgName, this.args.fromString(v)); } public async post(): Promise { @@ -61,7 +61,7 @@ export class ArrayCreateFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public name(v: string): void { - this.args.setString(ArgName, v); + this.args.set(ArgName, this.args.fromString(v)); } public async post(): Promise { @@ -76,15 +76,15 @@ export class ArraySetFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public index(v: wasmclient.Int32): void { - this.args.setInt32(ArgIndex, v); + this.args.set(ArgIndex, this.args.fromInt32(v)); } public name(v: string): void { - this.args.setString(ArgName, v); + this.args.set(ArgName, this.args.fromString(v)); } public value(v: string): void { - this.args.setString(ArgValue, v); + this.args.set(ArgValue, this.args.fromString(v)); } public async post(): Promise { @@ -101,79 +101,79 @@ export class ParamTypesFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public address(v: wasmclient.Address): void { - this.args.setAddress(ArgAddress, v); + this.args.set(ArgAddress, this.args.fromAddress(v)); } public agentID(v: wasmclient.AgentID): void { - this.args.setAgentID(ArgAgentID, v); + this.args.set(ArgAgentID, this.args.fromAgentID(v)); } public bool(v: boolean): void { - this.args.setBool(ArgBool, v); + this.args.set(ArgBool, this.args.fromBool(v)); } public bytes(v: wasmclient.Bytes): void { - this.args.setBytes(ArgBytes, v); + this.args.set(ArgBytes, this.args.fromBytes(v)); } public chainID(v: wasmclient.ChainID): void { - this.args.setChainID(ArgChainID, v); + this.args.set(ArgChainID, this.args.fromChainID(v)); } public color(v: wasmclient.Color): void { - this.args.setColor(ArgColor, v); + this.args.set(ArgColor, this.args.fromColor(v)); } public hash(v: wasmclient.Hash): void { - this.args.setHash(ArgHash, v); + this.args.set(ArgHash, this.args.fromHash(v)); } public hname(v: wasmclient.Hname): void { - this.args.setHname(ArgHname, v); + this.args.set(ArgHname, this.args.fromHname(v)); } public int16(v: wasmclient.Int16): void { - this.args.setInt16(ArgInt16, v); + this.args.set(ArgInt16, this.args.fromInt16(v)); } public int32(v: wasmclient.Int32): void { - this.args.setInt32(ArgInt32, v); + this.args.set(ArgInt32, this.args.fromInt32(v)); } public int64(v: wasmclient.Int64): void { - this.args.setInt64(ArgInt64, v); + this.args.set(ArgInt64, this.args.fromInt64(v)); } public int8(v: wasmclient.Int8): void { - this.args.setInt8(ArgInt8, v); + this.args.set(ArgInt8, this.args.fromInt8(v)); } public param(v: wasmclient.Bytes): void { - this.args.setBytes(ArgParam, v); + this.args.set(ArgParam, this.args.fromBytes(v)); } public requestID(v: wasmclient.RequestID): void { - this.args.setRequestID(ArgRequestID, v); + this.args.set(ArgRequestID, this.args.fromRequestID(v)); } public string(v: string): void { - this.args.setString(ArgString, v); + this.args.set(ArgString, this.args.fromString(v)); } public uint16(v: wasmclient.Uint16): void { - this.args.setUint16(ArgUint16, v); + this.args.set(ArgUint16, this.args.fromUint16(v)); } public uint32(v: wasmclient.Uint32): void { - this.args.setUint32(ArgUint32, v); + this.args.set(ArgUint32, this.args.fromUint32(v)); } public uint64(v: wasmclient.Uint64): void { - this.args.setUint64(ArgUint64, v); + this.args.set(ArgUint64, this.args.fromUint64(v)); } public uint8(v: wasmclient.Uint8): void { - this.args.setUint8(ArgUint8, v); + this.args.set(ArgUint8, this.args.fromUint8(v)); } public async post(): Promise { @@ -196,11 +196,11 @@ export class TriggerEventFunc extends wasmclient.ClientFunc { private args: wasmclient.Arguments = new wasmclient.Arguments(); public address(v: wasmclient.Address): void { - this.args.setAddress(ArgAddress, v); + this.args.set(ArgAddress, this.args.fromAddress(v)); } public name(v: string): void { - this.args.setString(ArgName, v); + this.args.set(ArgName, this.args.fromString(v)); } public async post(): Promise { @@ -216,19 +216,21 @@ export class ArrayLengthView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public name(v: string): void { - this.args.setString(ArgName, v); + this.args.set(ArgName, this.args.fromString(v)); } public async call(): Promise { this.args.mandatory(ArgName); - return new ArrayLengthResults(await this.callView("arrayLength", this.args)); + const res = new ArrayLengthResults(); + await this.callView("arrayLength", this.args, res); + return res; } } -export class ArrayLengthResults extends wasmclient.ViewResults { +export class ArrayLengthResults extends wasmclient.Results { length(): wasmclient.Int32 { - return this.res.getInt32(ResLength); + return this.toInt32(this.get(ResLength)); } } @@ -238,24 +240,26 @@ export class ArrayValueView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public index(v: wasmclient.Int32): void { - this.args.setInt32(ArgIndex, v); + this.args.set(ArgIndex, this.args.fromInt32(v)); } public name(v: string): void { - this.args.setString(ArgName, v); + this.args.set(ArgName, this.args.fromString(v)); } public async call(): Promise { this.args.mandatory(ArgIndex); this.args.mandatory(ArgName); - return new ArrayValueResults(await this.callView("arrayValue", this.args)); + const res = new ArrayValueResults(); + await this.callView("arrayValue", this.args, res); + return res; } } -export class ArrayValueResults extends wasmclient.ViewResults { +export class ArrayValueResults extends wasmclient.Results { value(): string { - return this.res.getString(ResValue); + return this.toString(this.get(ResValue)); } } @@ -265,24 +269,26 @@ export class BlockRecordView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public blockIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgBlockIndex, v); + this.args.set(ArgBlockIndex, this.args.fromInt32(v)); } public recordIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgRecordIndex, v); + this.args.set(ArgRecordIndex, this.args.fromInt32(v)); } public async call(): Promise { this.args.mandatory(ArgBlockIndex); this.args.mandatory(ArgRecordIndex); - return new BlockRecordResults(await this.callView("blockRecord", this.args)); + const res = new BlockRecordResults(); + await this.callView("blockRecord", this.args, res); + return res; } } -export class BlockRecordResults extends wasmclient.ViewResults { +export class BlockRecordResults extends wasmclient.Results { record(): wasmclient.Bytes { - return this.res.getBytes(ResRecord); + return this.toBytes(this.get(ResRecord)); } } @@ -292,19 +298,21 @@ export class BlockRecordsView extends wasmclient.ClientView { private args: wasmclient.Arguments = new wasmclient.Arguments(); public blockIndex(v: wasmclient.Int32): void { - this.args.setInt32(ArgBlockIndex, v); + this.args.set(ArgBlockIndex, this.args.fromInt32(v)); } public async call(): Promise { this.args.mandatory(ArgBlockIndex); - return new BlockRecordsResults(await this.callView("blockRecords", this.args)); + const res = new BlockRecordsResults(); + await this.callView("blockRecords", this.args, res); + return res; } } -export class BlockRecordsResults extends wasmclient.ViewResults { +export class BlockRecordsResults extends wasmclient.Results { count(): wasmclient.Int32 { - return this.res.getInt32(ResCount); + return this.toInt32(this.get(ResCount)); } } @@ -313,14 +321,16 @@ export class BlockRecordsResults extends wasmclient.ViewResults { export class GetRandomView extends wasmclient.ClientView { public async call(): Promise { - return new GetRandomResults(await this.callView("getRandom", null)); + const res = new GetRandomResults(); + await this.callView("getRandom", null, res); + return res; } } -export class GetRandomResults extends wasmclient.ViewResults { +export class GetRandomResults extends wasmclient.Results { random(): wasmclient.Int64 { - return this.res.getInt64(ResRandom); + return this.toInt64(this.get(ResRandom)); } } @@ -329,14 +339,16 @@ export class GetRandomResults extends wasmclient.ViewResults { export class IotaBalanceView extends wasmclient.ClientView { public async call(): Promise { - return new IotaBalanceResults(await this.callView("iotaBalance", null)); + const res = new IotaBalanceResults(); + await this.callView("iotaBalance", null, res); + return res; } } -export class IotaBalanceResults extends wasmclient.ViewResults { +export class IotaBalanceResults extends wasmclient.Results { iotas(): wasmclient.Int64 { - return this.res.getInt64(ResIotas); + return this.toInt64(this.get(ResIotas)); } } From e69e51f834f395103d7993a915a4e126dd2bb54b Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Mon, 10 Jan 2022 17:38:31 -0800 Subject: [PATCH 093/120] Disabled one more client test --- contracts/wasm/testwasmlib/test/testwasmlib_client_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go index daff531971..f96da7f4b4 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_client_test.go @@ -120,6 +120,9 @@ func TestClientArray(t *testing.T) { } func TestAccountBalance(t *testing.T) { + // for now skip client tests + t.SkipNow() + // we're testing against wasp-cluster, so defaults will do svcClient := wasmclient.DefaultServiceClient() From 106d46800a45b612fecea863009745e9cc2d4951 Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Tue, 11 Jan 2022 12:52:22 +0200 Subject: [PATCH 094/120] Consensus workflow flags separated to new object --- packages/chain/consensus/action.go | 65 +++++---- packages/chain/consensus/consensus.go | 15 +- packages/chain/consensus/workflow_status.go | 144 ++++++++++++++++++++ 3 files changed, 177 insertions(+), 47 deletions(-) create mode 100644 packages/chain/consensus/workflow_status.go diff --git a/packages/chain/consensus/action.go b/packages/chain/consensus/action.go index 0f7bd5e9ab..ee9b21cdea 100644 --- a/packages/chain/consensus/action.go +++ b/packages/chain/consensus/action.go @@ -27,9 +27,9 @@ import ( // takeAction triggers actions whenever relevant func (c *consensus) takeAction() { - if !c.workflow.stateReceived || !c.workflow.inProgress { + if !c.workflow.IsStateReceived() || !c.workflow.IsInProgress() { c.log.Debugf("takeAction skipped: stateReceived: %v, workflow in progress: %v", - c.workflow.stateReceived, c.workflow.inProgress) + c.workflow.IsStateReceived(), c.workflow.IsInProgress()) return } @@ -44,11 +44,11 @@ func (c *consensus) takeAction() { // proposeBatchIfNeeded when non empty ready batch is available is in mempool propose it as a candidate // for the ACS agreement func (c *consensus) proposeBatchIfNeeded() { - if c.workflow.batchProposalSent { + if c.workflow.IsBatchProposalSent() { c.log.Debugf("proposeBatch not needed: batch proposal already sent") return } - if c.workflow.consensusBatchKnown { + if c.workflow.IsConsensusBatchKnown() { c.log.Debugf("proposeBatch not needed: consensus batch already known") return } @@ -78,19 +78,19 @@ func (c *consensus) proposeBatchIfNeeded() { c.log.Infof("proposeBatch: proposed batch len = %d, ACS session ID: %d, state index: %d", len(reqs), c.acsSessionID, c.stateOutput.GetStateIndex()) - c.workflow.batchProposalSent = true + c.workflow.setBatchProposalSent() } // runVMIfNeeded attempts to extract deterministic batch of requests from ACS. // If it succeeds (i.e. all requests are available) and the extracted batch is nonempty, it runs the request func (c *consensus) runVMIfNeeded() { - if !c.workflow.consensusBatchKnown { + if !c.workflow.IsConsensusBatchKnown() { c.log.Debugf("runVM not needed: consensus batch is not known") return } - if c.workflow.vmStarted || c.workflow.vmResultSigned { + if c.workflow.IsVMStarted() || c.workflow.IsVMResultSigned() { c.log.Debugf("runVM not needed: vmStarted %v, vmResultSigned %v", - c.workflow.vmStarted, c.workflow.vmResultSigned) + c.workflow.IsVMStarted(), c.workflow.IsVMResultSigned()) return } if time.Now().Before(c.delayRunVMUntil) { @@ -147,7 +147,7 @@ func (c *consensus) runVMIfNeeded() { "block index", vmTask.ChainInput.GetStateIndex(), "num req", len(vmTask.Requests), ) - c.workflow.vmStarted = true + c.workflow.setVMStarted() vmTask.StartTime = time.Now() c.consensusMetrics.CountVMRuns() go c.vmRunner.Run(vmTask) @@ -252,7 +252,7 @@ func (c *consensus) prepareVMTask(reqs []iscp.Request) *vm.VMTask { } func (c *consensus) broadcastSignedResultIfNeeded() { - if !c.workflow.vmResultSigned { + if !c.workflow.IsVMResultSigned() { c.log.Debugf("broadcastSignedResult not needed: vm result is not signed") return } @@ -285,11 +285,11 @@ func (c *consensus) broadcastSignedResultIfNeeded() { // the transaction to L1. The deadline por posting is set proportionally to the sequence number (deterministic) // If the node sees the transaction of the L1 before its deadline, it cancels its posting func (c *consensus) checkQuorum() { - if c.workflow.transactionFinalized { + if c.workflow.IsTransactionFinalized() { c.log.Debugf("checkQuorum not needed: transaction already finalized") return } - if !c.workflow.vmResultSigned { + if !c.workflow.IsVMResultSigned() { // only can aggregate signatures if own result is calculated c.log.Debugf("checkQuorum not needed: vm result is not signed") return @@ -363,13 +363,13 @@ func (c *consensus) checkQuorum() { } else { c.log.Debugf("checkQuorum: finalized tx %s, iAmContributor: false", tx.ID().Base58()) } - c.workflow.transactionFinalized = true + c.workflow.setTransactionFinalized() c.pullInclusionStateDeadline = time.Now() } // postTransactionIfNeeded posts a finalized transaction upon deadline unless it was evidenced on L1 before the deadline. func (c *consensus) postTransactionIfNeeded() { - if !c.workflow.transactionFinalized { + if !c.workflow.IsTransactionFinalized() { c.log.Debugf("postTransaction not needed: transaction is not finalized") return } @@ -378,11 +378,11 @@ func (c *consensus) postTransactionIfNeeded() { c.log.Debugf("postTransaction not needed: i am not a contributor") return } - if c.workflow.transactionPosted { + if c.workflow.IsTransactionPosted() { c.log.Debugf("postTransaction not needed: transaction already posted") return } - if c.workflow.transactionSeen { + if c.workflow.IsTransactionSeen() { c.log.Debugf("postTransaction not needed: transaction already seen") return } @@ -392,18 +392,18 @@ func (c *consensus) postTransactionIfNeeded() { } go c.nodeConn.PostTransaction(c.finalTx) - c.workflow.transactionPosted = true // TODO: Fix it, retries should be in place for robustness. + c.workflow.setTransactionPosted() // TODO: Fix it, retries should be in place for robustness. c.log.Infof("postTransaction: POSTED TRANSACTION: %s, number of inputs: %d, outputs: %d", c.finalTx.ID().Base58(), len(c.finalTx.Essence().Inputs()), len(c.finalTx.Essence().Outputs())) } // pullInclusionStateIfNeeded periodic pull to know the inclusions state of the transaction. Note that pulling // starts immediately after finalization of the transaction, not after posting it func (c *consensus) pullInclusionStateIfNeeded() { - if !c.workflow.transactionFinalized { + if !c.workflow.IsTransactionFinalized() { c.log.Debugf("pullInclusionState not needed: transaction is not finalized") return } - if c.workflow.transactionSeen { + if c.workflow.IsTransactionSeen() { c.log.Debugf("pullInclusionState not needed: transaction already seen") return } @@ -456,7 +456,7 @@ func (c *consensus) receiveACS(values [][]byte, sessionID uint64) { c.log.Debugf("receiveACS: session id missmatch: expected %v, received %v", c.acsSessionID, sessionID) return } - if c.workflow.consensusBatchKnown { + if c.workflow.IsConsensusBatchKnown() { // should not happen c.log.Debugf("receiveACS: consensus batch already known (should not happen)") return @@ -552,7 +552,7 @@ func (c *consensus) receiveACS(values [][]byte, sessionID uint64) { c.myContributionSeqNumber = myContributionSeqNumber c.contributors = contributors - c.workflow.consensusBatchKnown = true + c.workflow.setConsensusBatchKnown() if c.iAmContributor { c.log.Debugf("receiveACS: ACS received. Contributors to ACS: %+v, iAmContributor: true, seqnr: %d, reqs: %+v", @@ -566,7 +566,7 @@ func (c *consensus) receiveACS(values [][]byte, sessionID uint64) { } func (c *consensus) processInclusionState(msg *messages.InclusionStateMsg) { - if !c.workflow.transactionFinalized { + if !c.workflow.IsTransactionFinalized() { c.log.Debugf("processInclusionState: transaction not finalized -> skipping.") return } @@ -577,15 +577,15 @@ func (c *consensus) processInclusionState(msg *messages.InclusionStateMsg) { } switch msg.State { case ledgerstate.Pending: - c.workflow.transactionSeen = true + c.workflow.setTransactionSeen() c.log.Debugf("processInclusionState: transaction id %v is pending.", c.finalTx.ID().Base58()) case ledgerstate.Confirmed: - c.workflow.transactionSeen = true - c.workflow.inProgress = false + c.workflow.setTransactionSeen() + c.workflow.setCompleted() c.refreshConsensusInfo() c.log.Debugf("processInclusionState: transaction id %s is confirmed; workflow finished", msg.TxID.Base58()) case ledgerstate.Rejected: - c.workflow.transactionSeen = true + c.workflow.setTransactionSeen() c.log.Infof("processInclusionState: transaction id %s is rejected; restarting consensus.", msg.TxID.Base58()) c.resetWorkflow() } @@ -668,20 +668,17 @@ func (c *consensus) resetWorkflow() { c.consensusBatch = nil c.contributors = nil c.resultSigAck = c.resultSigAck[:0] - c.workflow = workflowFlags{ - stateReceived: c.stateOutput != nil, - inProgress: c.stateOutput != nil, - } + c.workflow = newWorkflowStatus(c.stateOutput != nil) c.log.Debugf("Workflow reset") } func (c *consensus) processVMResult(result *vm.VMTask) { - if !c.workflow.vmStarted || - c.workflow.vmResultSigned || + if !c.workflow.IsVMStarted() || + c.workflow.IsVMResultSigned() || c.acsSessionID != result.ACSSessionID { // out of context c.log.Debugf("processVMResult: out of context vmStarted %v, vmResultSignedAndBroadcasted %v, expected ACS session ID %v, returned ACS session ID %v", - c.workflow.vmStarted, c.workflow.vmResultSigned, c.acsSessionID, result.ACSSessionID) + c.workflow.IsVMStarted(), c.workflow.IsVMResultSigned(), c.acsSessionID, result.ACSSessionID) return } rotation := result.RotationAddress != nil @@ -713,7 +710,7 @@ func (c *consensus) processVMResult(result *vm.VMTask) { SenderIndex: c.committee.OwnPeerIndex(), } - c.workflow.vmResultSigned = true + c.workflow.setVMResultSigned() c.log.Debugf("processVMResult signed: essence hash: %s", essenceHash.String()) } diff --git a/packages/chain/consensus/consensus.go b/packages/chain/consensus/consensus.go index ea579de2d8..169057d5af 100644 --- a/packages/chain/consensus/consensus.go +++ b/packages/chain/consensus/consensus.go @@ -40,7 +40,7 @@ type consensus struct { iAmContributor bool myContributionSeqNumber uint16 contributors []uint16 - workflow workflowFlags + workflow *WorkflowStatus delayBatchProposalUntil time.Time delayRunVMUntil time.Time delaySendingSignedResult time.Time @@ -70,18 +70,6 @@ type consensus struct { consensusMetrics metrics.ConsensusMetrics } -type workflowFlags struct { - stateReceived bool - batchProposalSent bool - consensusBatchKnown bool - vmStarted bool - vmResultSigned bool - transactionFinalized bool - transactionPosted bool - transactionSeen bool - inProgress bool -} - var _ chain.Consensus = &consensus{} const ( @@ -106,6 +94,7 @@ func New(chainCore chain.ChainCore, mempool chain.Mempool, committee chain.Commi mempool: mempool, nodeConn: nodeConn, vmRunner: runvm.NewVMRunner(), + workflow: newWorkflowStatus(false), resultSignatures: make([]*messages.SignedResultMsgIn, committee.Size()), resultSigAck: make([]uint16, 0, committee.Size()), timers: timers, diff --git a/packages/chain/consensus/workflow_status.go b/packages/chain/consensus/workflow_status.go new file mode 100644 index 0000000000..e19f437bb0 --- /dev/null +++ b/packages/chain/consensus/workflow_status.go @@ -0,0 +1,144 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package consensus + +import ( + "time" +) + +type WorkflowStatus struct { + flagStateReceived bool + flagBatchProposalSent bool + flagConsensusBatchKnown bool + flagVMStarted bool + flagVMResultSigned bool + flagTransactionFinalized bool + flagTransactionPosted bool + flagTransactionSeen bool + flagInProgress bool + + timeBatchProposalSent time.Time + timeConsensusBatchKnown time.Time + timeVMStarted time.Time + timeVMResultSigned time.Time + timeTransactionFinalized time.Time + timeTransactionPosted time.Time + timeTransactionSeen time.Time + timeCompleted time.Time +} + +func newWorkflowStatus(stateReceived bool) *WorkflowStatus { + return &WorkflowStatus{ + flagStateReceived: stateReceived, + flagInProgress: stateReceived, + } +} + +func (wsT *WorkflowStatus) setBatchProposalSent() { + wsT.flagBatchProposalSent = true + wsT.timeBatchProposalSent = time.Now() +} + +func (wsT *WorkflowStatus) setConsensusBatchKnown() { + wsT.flagConsensusBatchKnown = true + wsT.timeConsensusBatchKnown = time.Now() +} + +func (wsT *WorkflowStatus) setVMStarted() { + wsT.flagVMStarted = true + wsT.timeVMStarted = time.Now() +} + +func (wsT *WorkflowStatus) setVMResultSigned() { + wsT.flagVMResultSigned = true + wsT.timeVMResultSigned = time.Now() +} + +func (wsT *WorkflowStatus) setTransactionFinalized() { + wsT.flagTransactionFinalized = true + wsT.timeTransactionFinalized = time.Now() +} + +func (wsT *WorkflowStatus) setTransactionPosted() { + wsT.flagTransactionPosted = true + wsT.timeTransactionPosted = time.Now() +} + +func (wsT *WorkflowStatus) setTransactionSeen() { + wsT.flagTransactionSeen = true + wsT.timeTransactionSeen = time.Now() +} + +func (wsT *WorkflowStatus) setCompleted() { + wsT.flagInProgress = false + wsT.timeCompleted = time.Now() +} + +func (wsT *WorkflowStatus) IsStateReceived() bool { + return wsT.flagStateReceived +} + +func (wsT *WorkflowStatus) IsBatchProposalSent() bool { + return wsT.flagBatchProposalSent +} + +func (wsT *WorkflowStatus) IsConsensusBatchKnown() bool { + return wsT.flagConsensusBatchKnown +} + +func (wsT *WorkflowStatus) IsVMStarted() bool { + return wsT.flagVMStarted +} + +func (wsT *WorkflowStatus) IsVMResultSigned() bool { + return wsT.flagVMResultSigned +} + +func (wsT *WorkflowStatus) IsTransactionFinalized() bool { + return wsT.flagTransactionFinalized +} + +func (wsT *WorkflowStatus) IsTransactionPosted() bool { + return wsT.flagTransactionPosted +} + +func (wsT *WorkflowStatus) IsTransactionSeen() bool { + return wsT.flagTransactionSeen +} + +func (wsT *WorkflowStatus) IsInProgress() bool { + return wsT.flagInProgress +} + +func (wsT *WorkflowStatus) GetBatchProposalSentTime() time.Time { + return wsT.timeBatchProposalSent +} + +func (wsT *WorkflowStatus) GetConsensusBatchKnownTime() time.Time { + return wsT.timeConsensusBatchKnown +} + +func (wsT *WorkflowStatus) GetVMStartedTime() time.Time { + return wsT.timeVMStarted +} + +func (wsT *WorkflowStatus) GetVMResultSignedTime() time.Time { + return wsT.timeVMResultSigned +} + +func (wsT *WorkflowStatus) GetTransactionFinalizedTime() time.Time { + return wsT.timeTransactionFinalized +} + +func (wsT *WorkflowStatus) GetTransactionPostedTime() time.Time { + return wsT.timeTransactionPosted +} + +func (wsT *WorkflowStatus) GetTransactionSeenTime() time.Time { + return wsT.timeTransactionSeen +} + +func (wsT *WorkflowStatus) GetCompletedTime() time.Time { + return wsT.timeCompleted +} From 3dc0b17431b34c87627101eb98eec3c19d8bfde9 Mon Sep 17 00:00:00 2001 From: Eric Hop Date: Tue, 11 Jan 2022 04:29:02 -0800 Subject: [PATCH 095/120] Processed PR comments --- contracts/wasm/erc20/ts/erc20/contract.ts | 8 +- contracts/wasm/erc721/ts/erc721/contract.ts | 14 +-- .../fairroulette/ts/fairroulette/contract.ts | 10 +- .../wasm/testwasmlib/go/testwasmlib/consts.go | 10 ++ .../testwasmlib/go/testwasmlib/contract.go | 45 +++++++ .../wasm/testwasmlib/go/testwasmlib/keys.go | 42 ++++--- .../wasm/testwasmlib/go/testwasmlib/lib.go | 94 +++++++++++++++ .../wasm/testwasmlib/go/testwasmlib/params.go | 88 ++++++++++++++ .../testwasmlib/go/testwasmlib/results.go | 16 +++ .../wasm/testwasmlib/go/testwasmlib/state.go | 32 +++++ .../testwasmlib/go/testwasmlib/testwasmlib.go | 27 +++++ .../testwasmlib/go/testwasmlib/typedefs.go | 24 ++++ .../go/testwasmlibclient/service.go | 105 ++++++++++++++++ contracts/wasm/testwasmlib/schema.yaml | 23 ++++ contracts/wasm/testwasmlib/src/consts.rs | 10 ++ contracts/wasm/testwasmlib/src/contract.rs | 58 +++++++++ contracts/wasm/testwasmlib/src/keys.rs | 42 ++++--- contracts/wasm/testwasmlib/src/lib.rs | 97 +++++++++++++++ contracts/wasm/testwasmlib/src/params.rs | 112 ++++++++++++++++++ contracts/wasm/testwasmlib/src/results.rs | 22 ++++ contracts/wasm/testwasmlib/src/state.rs | 38 ++++++ contracts/wasm/testwasmlib/src/testwasmlib.rs | 12 ++ contracts/wasm/testwasmlib/src/typedefs.rs | 30 +++++ .../wasm/testwasmlib/test/testwasmlib_bg.wasm | Bin 43872 -> 44734 bytes .../wasm/testwasmlib/test/testwasmlib_test.go | 50 ++++++++ .../wasm/testwasmlib/ts/testwasmlib/consts.ts | 10 ++ .../testwasmlib/ts/testwasmlib/contract.ts | 81 ++++++++++++- .../wasm/testwasmlib/ts/testwasmlib/keys.ts | 40 ++++--- .../wasm/testwasmlib/ts/testwasmlib/lib.ts | 48 ++++++++ .../wasm/testwasmlib/ts/testwasmlib/params.ts | 72 +++++++++++ .../testwasmlib/ts/testwasmlib/results.ts | 12 ++ .../wasm/testwasmlib/ts/testwasmlib/state.ts | 40 +++++++ .../testwasmlib/ts/testwasmlib/testwasmlib.ts | 12 ++ .../testwasmlib/ts/testwasmlib/typedefs.ts | 34 ++++++ .../ts/testwasmlibclient/service.ts | 101 ++++++++++++++++ .../ts/wasmclient/coreaccounts/service.ts | 8 +- .../wasmlib/ts/wasmclient/coreblob/service.ts | 6 +- .../ts/wasmclient/coreblocklog/service.ts | 20 ++-- .../ts/wasmclient/coregovernance/service.ts | 8 +- .../wasmlib/ts/wasmclient/coreroot/service.ts | 4 +- .../generator/tsclienttemplates/service.go | 2 +- .../schema/generator/tstemplates/contract.go | 2 +- 42 files changed, 1406 insertions(+), 103 deletions(-) diff --git a/contracts/wasm/erc20/ts/erc20/contract.ts b/contracts/wasm/erc20/ts/erc20/contract.ts index 111b9e6733..d04ec0afa0 100644 --- a/contracts/wasm/erc20/ts/erc20/contract.ts +++ b/contracts/wasm/erc20/ts/erc20/contract.ts @@ -14,7 +14,7 @@ export class ApproveCall { } export class ApproveContext { - events: sc.Erc20Events = new sc.Erc20Events(); + events: sc.Erc20Events = new sc.Erc20Events(); params: sc.ImmutableApproveParams = new sc.ImmutableApproveParams(); state: sc.MutableErc20State = new sc.MutableErc20State(); } @@ -25,7 +25,7 @@ export class InitCall { } export class InitContext { - events: sc.Erc20Events = new sc.Erc20Events(); + events: sc.Erc20Events = new sc.Erc20Events(); params: sc.ImmutableInitParams = new sc.ImmutableInitParams(); state: sc.MutableErc20State = new sc.MutableErc20State(); } @@ -36,7 +36,7 @@ export class TransferCall { } export class TransferContext { - events: sc.Erc20Events = new sc.Erc20Events(); + events: sc.Erc20Events = new sc.Erc20Events(); params: sc.ImmutableTransferParams = new sc.ImmutableTransferParams(); state: sc.MutableErc20State = new sc.MutableErc20State(); } @@ -47,7 +47,7 @@ export class TransferFromCall { } export class TransferFromContext { - events: sc.Erc20Events = new sc.Erc20Events(); + events: sc.Erc20Events = new sc.Erc20Events(); params: sc.ImmutableTransferFromParams = new sc.ImmutableTransferFromParams(); state: sc.MutableErc20State = new sc.MutableErc20State(); } diff --git a/contracts/wasm/erc721/ts/erc721/contract.ts b/contracts/wasm/erc721/ts/erc721/contract.ts index 4f33962168..e14506cc23 100644 --- a/contracts/wasm/erc721/ts/erc721/contract.ts +++ b/contracts/wasm/erc721/ts/erc721/contract.ts @@ -14,7 +14,7 @@ export class ApproveCall { } export class ApproveContext { - events: sc.Erc721Events = new sc.Erc721Events(); + events: sc.Erc721Events = new sc.Erc721Events(); params: sc.ImmutableApproveParams = new sc.ImmutableApproveParams(); state: sc.MutableErc721State = new sc.MutableErc721State(); } @@ -25,7 +25,7 @@ export class BurnCall { } export class BurnContext { - events: sc.Erc721Events = new sc.Erc721Events(); + events: sc.Erc721Events = new sc.Erc721Events(); params: sc.ImmutableBurnParams = new sc.ImmutableBurnParams(); state: sc.MutableErc721State = new sc.MutableErc721State(); } @@ -36,7 +36,7 @@ export class InitCall { } export class InitContext { - events: sc.Erc721Events = new sc.Erc721Events(); + events: sc.Erc721Events = new sc.Erc721Events(); params: sc.ImmutableInitParams = new sc.ImmutableInitParams(); state: sc.MutableErc721State = new sc.MutableErc721State(); } @@ -47,7 +47,7 @@ export class MintCall { } export class MintContext { - events: sc.Erc721Events = new sc.Erc721Events(); + events: sc.Erc721Events = new sc.Erc721Events(); params: sc.ImmutableMintParams = new sc.ImmutableMintParams(); state: sc.MutableErc721State = new sc.MutableErc721State(); } @@ -58,7 +58,7 @@ export class SafeTransferFromCall { } export class SafeTransferFromContext { - events: sc.Erc721Events = new sc.Erc721Events(); + events: sc.Erc721Events = new sc.Erc721Events(); params: sc.ImmutableSafeTransferFromParams = new sc.ImmutableSafeTransferFromParams(); state: sc.MutableErc721State = new sc.MutableErc721State(); } @@ -69,7 +69,7 @@ export class SetApprovalForAllCall { } export class SetApprovalForAllContext { - events: sc.Erc721Events = new sc.Erc721Events(); + events: sc.Erc721Events = new sc.Erc721Events(); params: sc.ImmutableSetApprovalForAllParams = new sc.ImmutableSetApprovalForAllParams(); state: sc.MutableErc721State = new sc.MutableErc721State(); } @@ -80,7 +80,7 @@ export class TransferFromCall { } export class TransferFromContext { - events: sc.Erc721Events = new sc.Erc721Events(); + events: sc.Erc721Events = new sc.Erc721Events(); params: sc.ImmutableTransferFromParams = new sc.ImmutableTransferFromParams(); state: sc.MutableErc721State = new sc.MutableErc721State(); } diff --git a/contracts/wasm/fairroulette/ts/fairroulette/contract.ts b/contracts/wasm/fairroulette/ts/fairroulette/contract.ts index c612394c71..7b9b6e4bc2 100644 --- a/contracts/wasm/fairroulette/ts/fairroulette/contract.ts +++ b/contracts/wasm/fairroulette/ts/fairroulette/contract.ts @@ -13,7 +13,7 @@ export class ForcePayoutCall { } export class ForcePayoutContext { - events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); + events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); } @@ -22,7 +22,7 @@ export class ForceResetCall { } export class ForceResetContext { - events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); + events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); } @@ -31,7 +31,7 @@ export class PayWinnersCall { } export class PayWinnersContext { - events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); + events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); } @@ -41,7 +41,7 @@ export class PlaceBetCall { } export class PlaceBetContext { - events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); + events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); params: sc.ImmutablePlaceBetParams = new sc.ImmutablePlaceBetParams(); state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); } @@ -52,7 +52,7 @@ export class PlayPeriodCall { } export class PlayPeriodContext { - events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); + events: sc.FairRouletteEvents = new sc.FairRouletteEvents(); params: sc.ImmutablePlayPeriodParams = new sc.ImmutablePlayPeriodParams(); state: sc.MutableFairRouletteState = new sc.MutableFairRouletteState(); } diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/consts.go b/contracts/wasm/testwasmlib/go/testwasmlib/consts.go index a6fc962005..7b8cefa6f3 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/consts.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/consts.go @@ -30,6 +30,7 @@ const ( ParamInt32 = "int32" ParamInt64 = "int64" ParamInt8 = "int8" + ParamKey = "key" ParamName = "name" ParamParam = "this" ParamRecordIndex = "recordIndex" @@ -53,6 +54,7 @@ const ( const ( StateArrays = "arrays" + StateMaps = "maps" StateRandom = "random" ) @@ -60,6 +62,9 @@ const ( FuncArrayClear = "arrayClear" FuncArrayCreate = "arrayCreate" FuncArraySet = "arraySet" + FuncMapClear = "mapClear" + FuncMapCreate = "mapCreate" + FuncMapSet = "mapSet" FuncParamTypes = "paramTypes" FuncRandom = "random" FuncTriggerEvent = "triggerEvent" @@ -69,12 +74,16 @@ const ( ViewBlockRecords = "blockRecords" ViewGetRandom = "getRandom" ViewIotaBalance = "iotaBalance" + ViewMapValue = "mapValue" ) const ( HFuncArrayClear = wasmlib.ScHname(0x88021821) HFuncArrayCreate = wasmlib.ScHname(0x1ed5b23b) HFuncArraySet = wasmlib.ScHname(0x2c4150b3) + HFuncMapClear = wasmlib.ScHname(0x027f215a) + HFuncMapCreate = wasmlib.ScHname(0x6295d599) + HFuncMapSet = wasmlib.ScHname(0xf2260404) HFuncParamTypes = wasmlib.ScHname(0x6921c4cd) HFuncRandom = wasmlib.ScHname(0xe86c97ca) HFuncTriggerEvent = wasmlib.ScHname(0xd5438ac6) @@ -84,4 +93,5 @@ const ( HViewBlockRecords = wasmlib.ScHname(0x16e249ea) HViewGetRandom = wasmlib.ScHname(0x46263045) HViewIotaBalance = wasmlib.ScHname(0x9d3920bd) + HViewMapValue = wasmlib.ScHname(0x23149bef) ) diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/contract.go b/contracts/wasm/testwasmlib/go/testwasmlib/contract.go index 7d1d33c83f..87309712b0 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/contract.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/contract.go @@ -24,6 +24,21 @@ type ArraySetCall struct { Params MutableArraySetParams } +type MapClearCall struct { + Func *wasmlib.ScFunc + Params MutableMapClearParams +} + +type MapCreateCall struct { + Func *wasmlib.ScFunc + Params MutableMapCreateParams +} + +type MapSetCall struct { + Func *wasmlib.ScFunc + Params MutableMapSetParams +} + type ParamTypesCall struct { Func *wasmlib.ScFunc Params MutableParamTypesParams @@ -72,6 +87,12 @@ type IotaBalanceCall struct { Results ImmutableIotaBalanceResults } +type MapValueCall struct { + Func *wasmlib.ScView + Params MutableMapValueParams + Results ImmutableMapValueResults +} + type Funcs struct{} var ScFuncs Funcs @@ -94,6 +115,24 @@ func (sc Funcs) ArraySet(ctx wasmlib.ScFuncCallContext) *ArraySetCall { return f } +func (sc Funcs) MapClear(ctx wasmlib.ScFuncCallContext) *MapClearCall { + f := &MapClearCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncMapClear)} + f.Func.SetPtrs(&f.Params.id, nil) + return f +} + +func (sc Funcs) MapCreate(ctx wasmlib.ScFuncCallContext) *MapCreateCall { + f := &MapCreateCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncMapCreate)} + f.Func.SetPtrs(&f.Params.id, nil) + return f +} + +func (sc Funcs) MapSet(ctx wasmlib.ScFuncCallContext) *MapSetCall { + f := &MapSetCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncMapSet)} + f.Func.SetPtrs(&f.Params.id, nil) + return f +} + func (sc Funcs) ParamTypes(ctx wasmlib.ScFuncCallContext) *ParamTypesCall { f := &ParamTypesCall{Func: wasmlib.NewScFunc(ctx, HScName, HFuncParamTypes)} f.Func.SetPtrs(&f.Params.id, nil) @@ -145,3 +184,9 @@ func (sc Funcs) IotaBalance(ctx wasmlib.ScViewCallContext) *IotaBalanceCall { f.Func.SetPtrs(nil, &f.Results.id) return f } + +func (sc Funcs) MapValue(ctx wasmlib.ScViewCallContext) *MapValueCall { + f := &MapValueCall{Func: wasmlib.NewScView(ctx, HScName, HViewMapValue)} + f.Func.SetPtrs(&f.Params.id, &f.Results.id) + return f +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/keys.go b/contracts/wasm/testwasmlib/go/testwasmlib/keys.go index e5d6841858..9b5e7ac61f 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/keys.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/keys.go @@ -24,29 +24,31 @@ const ( IdxParamInt32 = 11 IdxParamInt64 = 12 IdxParamInt8 = 13 - IdxParamName = 14 - IdxParamParam = 15 - IdxParamRecordIndex = 16 - IdxParamRequestID = 17 - IdxParamString = 18 - IdxParamUint16 = 19 - IdxParamUint32 = 20 - IdxParamUint64 = 21 - IdxParamUint8 = 22 - IdxParamValue = 23 + IdxParamKey = 14 + IdxParamName = 15 + IdxParamParam = 16 + IdxParamRecordIndex = 17 + IdxParamRequestID = 18 + IdxParamString = 19 + IdxParamUint16 = 20 + IdxParamUint32 = 21 + IdxParamUint64 = 22 + IdxParamUint8 = 23 + IdxParamValue = 24 - IdxResultCount = 24 - IdxResultIotas = 25 - IdxResultLength = 26 - IdxResultRandom = 27 - IdxResultRecord = 28 - IdxResultValue = 29 + IdxResultCount = 25 + IdxResultIotas = 26 + IdxResultLength = 27 + IdxResultRandom = 28 + IdxResultRecord = 29 + IdxResultValue = 30 - IdxStateArrays = 30 - IdxStateRandom = 31 + IdxStateArrays = 31 + IdxStateMaps = 32 + IdxStateRandom = 33 ) -const keyMapLen = 32 +const keyMapLen = 34 var keyMap = [keyMapLen]wasmlib.Key{ ParamAddress, @@ -63,6 +65,7 @@ var keyMap = [keyMapLen]wasmlib.Key{ ParamInt32, ParamInt64, ParamInt8, + ParamKey, ParamName, ParamParam, ParamRecordIndex, @@ -80,6 +83,7 @@ var keyMap = [keyMapLen]wasmlib.Key{ ResultRecord, ResultValue, StateArrays, + StateMaps, StateRandom, } diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/lib.go b/contracts/wasm/testwasmlib/go/testwasmlib/lib.go index fc9a32ca7d..1356e5135a 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/lib.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/lib.go @@ -14,6 +14,9 @@ func OnLoad() { exports.AddFunc(FuncArrayClear, funcArrayClearThunk) exports.AddFunc(FuncArrayCreate, funcArrayCreateThunk) exports.AddFunc(FuncArraySet, funcArraySetThunk) + exports.AddFunc(FuncMapClear, funcMapClearThunk) + exports.AddFunc(FuncMapCreate, funcMapCreateThunk) + exports.AddFunc(FuncMapSet, funcMapSetThunk) exports.AddFunc(FuncParamTypes, funcParamTypesThunk) exports.AddFunc(FuncRandom, funcRandomThunk) exports.AddFunc(FuncTriggerEvent, funcTriggerEventThunk) @@ -23,6 +26,7 @@ func OnLoad() { exports.AddView(ViewBlockRecords, viewBlockRecordsThunk) exports.AddView(ViewGetRandom, viewGetRandomThunk) exports.AddView(ViewIotaBalance, viewIotaBalanceThunk) + exports.AddView(ViewMapValue, viewMapValueThunk) for i, key := range keyMap { idxMap[i] = key.KeyID() @@ -94,6 +98,71 @@ func funcArraySetThunk(ctx wasmlib.ScFuncContext) { ctx.Log("testwasmlib.funcArraySet ok") } +type MapClearContext struct { + Events TestWasmLibEvents + Params ImmutableMapClearParams + State MutableTestWasmLibState +} + +func funcMapClearThunk(ctx wasmlib.ScFuncContext) { + ctx.Log("testwasmlib.funcMapClear") + f := &MapClearContext{ + Params: ImmutableMapClearParams{ + id: wasmlib.OBJ_ID_PARAMS, + }, + State: MutableTestWasmLibState{ + id: wasmlib.OBJ_ID_STATE, + }, + } + ctx.Require(f.Params.Name().Exists(), "missing mandatory name") + funcMapClear(ctx, f) + ctx.Log("testwasmlib.funcMapClear ok") +} + +type MapCreateContext struct { + Events TestWasmLibEvents + Params ImmutableMapCreateParams + State MutableTestWasmLibState +} + +func funcMapCreateThunk(ctx wasmlib.ScFuncContext) { + ctx.Log("testwasmlib.funcMapCreate") + f := &MapCreateContext{ + Params: ImmutableMapCreateParams{ + id: wasmlib.OBJ_ID_PARAMS, + }, + State: MutableTestWasmLibState{ + id: wasmlib.OBJ_ID_STATE, + }, + } + ctx.Require(f.Params.Name().Exists(), "missing mandatory name") + funcMapCreate(ctx, f) + ctx.Log("testwasmlib.funcMapCreate ok") +} + +type MapSetContext struct { + Events TestWasmLibEvents + Params ImmutableMapSetParams + State MutableTestWasmLibState +} + +func funcMapSetThunk(ctx wasmlib.ScFuncContext) { + ctx.Log("testwasmlib.funcMapSet") + f := &MapSetContext{ + Params: ImmutableMapSetParams{ + id: wasmlib.OBJ_ID_PARAMS, + }, + State: MutableTestWasmLibState{ + id: wasmlib.OBJ_ID_STATE, + }, + } + ctx.Require(f.Params.Key().Exists(), "missing mandatory key") + ctx.Require(f.Params.Name().Exists(), "missing mandatory name") + ctx.Require(f.Params.Value().Exists(), "missing mandatory value") + funcMapSet(ctx, f) + ctx.Log("testwasmlib.funcMapSet ok") +} + type ParamTypesContext struct { Events TestWasmLibEvents Params ImmutableParamTypesParams @@ -287,3 +356,28 @@ func viewIotaBalanceThunk(ctx wasmlib.ScViewContext) { viewIotaBalance(ctx, f) ctx.Log("testwasmlib.viewIotaBalance ok") } + +type MapValueContext struct { + Params ImmutableMapValueParams + Results MutableMapValueResults + State ImmutableTestWasmLibState +} + +func viewMapValueThunk(ctx wasmlib.ScViewContext) { + ctx.Log("testwasmlib.viewMapValue") + f := &MapValueContext{ + Params: ImmutableMapValueParams{ + id: wasmlib.OBJ_ID_PARAMS, + }, + Results: MutableMapValueResults{ + id: wasmlib.OBJ_ID_RESULTS, + }, + State: ImmutableTestWasmLibState{ + id: wasmlib.OBJ_ID_STATE, + }, + } + ctx.Require(f.Params.Key().Exists(), "missing mandatory key") + ctx.Require(f.Params.Name().Exists(), "missing mandatory name") + viewMapValue(ctx, f) + ctx.Log("testwasmlib.viewMapValue ok") +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/params.go b/contracts/wasm/testwasmlib/go/testwasmlib/params.go index 14ac37d564..dd89b42103 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/params.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/params.go @@ -73,6 +73,70 @@ func (s MutableArraySetParams) Value() wasmlib.ScMutableString { return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamValue)) } +type ImmutableMapClearParams struct { + id int32 +} + +func (s ImmutableMapClearParams) Name() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamName)) +} + +type MutableMapClearParams struct { + id int32 +} + +func (s MutableMapClearParams) Name() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamName)) +} + +type ImmutableMapCreateParams struct { + id int32 +} + +func (s ImmutableMapCreateParams) Name() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamName)) +} + +type MutableMapCreateParams struct { + id int32 +} + +func (s MutableMapCreateParams) Name() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamName)) +} + +type ImmutableMapSetParams struct { + id int32 +} + +func (s ImmutableMapSetParams) Key() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamKey)) +} + +func (s ImmutableMapSetParams) Name() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamName)) +} + +func (s ImmutableMapSetParams) Value() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamValue)) +} + +type MutableMapSetParams struct { + id int32 +} + +func (s MutableMapSetParams) Key() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamKey)) +} + +func (s MutableMapSetParams) Name() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamName)) +} + +func (s MutableMapSetParams) Value() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamValue)) +} + type MapStringToImmutableBytes struct { objID int32 } @@ -356,3 +420,27 @@ type MutableBlockRecordsParams struct { func (s MutableBlockRecordsParams) BlockIndex() wasmlib.ScMutableInt32 { return wasmlib.NewScMutableInt32(s.id, wasmlib.KeyID(ParamBlockIndex)) } + +type ImmutableMapValueParams struct { + id int32 +} + +func (s ImmutableMapValueParams) Key() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamKey)) +} + +func (s ImmutableMapValueParams) Name() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ParamName)) +} + +type MutableMapValueParams struct { + id int32 +} + +func (s MutableMapValueParams) Key() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamKey)) +} + +func (s MutableMapValueParams) Name() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ParamName)) +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/results.go b/contracts/wasm/testwasmlib/go/testwasmlib/results.go index 5638378ca7..4426bd9cea 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/results.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/results.go @@ -104,3 +104,19 @@ type MutableIotaBalanceResults struct { func (s MutableIotaBalanceResults) Iotas() wasmlib.ScMutableInt64 { return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(ResultIotas)) } + +type ImmutableMapValueResults struct { + id int32 +} + +func (s ImmutableMapValueResults) Value() wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(s.id, wasmlib.KeyID(ResultValue)) +} + +type MutableMapValueResults struct { + id int32 +} + +func (s MutableMapValueResults) Value() wasmlib.ScMutableString { + return wasmlib.NewScMutableString(s.id, wasmlib.KeyID(ResultValue)) +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/state.go b/contracts/wasm/testwasmlib/go/testwasmlib/state.go index a97190e37b..47bd6093a9 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/state.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/state.go @@ -18,6 +18,15 @@ func (m MapStringToImmutableStringArray) GetStringArray(key string) ImmutableStr return ImmutableStringArray{objID: subID} } +type MapStringToImmutableStringMap struct { + objID int32 +} + +func (m MapStringToImmutableStringMap) GetStringMap(key string) ImmutableStringMap { + subID := wasmlib.GetObjectID(m.objID, wasmlib.Key(key).KeyID(), wasmlib.TYPE_MAP) + return ImmutableStringMap{objID: subID} +} + type ImmutableTestWasmLibState struct { id int32 } @@ -27,6 +36,11 @@ func (s ImmutableTestWasmLibState) Arrays() MapStringToImmutableStringArray { return MapStringToImmutableStringArray{objID: mapID} } +func (s ImmutableTestWasmLibState) Maps() MapStringToImmutableStringMap { + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateMaps), wasmlib.TYPE_MAP) + return MapStringToImmutableStringMap{objID: mapID} +} + func (s ImmutableTestWasmLibState) Random() wasmlib.ScImmutableInt64 { return wasmlib.NewScImmutableInt64(s.id, wasmlib.KeyID(StateRandom)) } @@ -44,6 +58,19 @@ func (m MapStringToMutableStringArray) GetStringArray(key string) MutableStringA return MutableStringArray{objID: subID} } +type MapStringToMutableStringMap struct { + objID int32 +} + +func (m MapStringToMutableStringMap) Clear() { + wasmlib.Clear(m.objID) +} + +func (m MapStringToMutableStringMap) GetStringMap(key string) MutableStringMap { + subID := wasmlib.GetObjectID(m.objID, wasmlib.Key(key).KeyID(), wasmlib.TYPE_MAP) + return MutableStringMap{objID: subID} +} + type MutableTestWasmLibState struct { id int32 } @@ -57,6 +84,11 @@ func (s MutableTestWasmLibState) Arrays() MapStringToMutableStringArray { return MapStringToMutableStringArray{objID: mapID} } +func (s MutableTestWasmLibState) Maps() MapStringToMutableStringMap { + mapID := wasmlib.GetObjectID(s.id, wasmlib.KeyID(StateMaps), wasmlib.TYPE_MAP) + return MapStringToMutableStringMap{objID: mapID} +} + func (s MutableTestWasmLibState) Random() wasmlib.ScMutableInt64 { return wasmlib.NewScMutableInt64(s.id, wasmlib.KeyID(StateRandom)) } diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go b/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go index 2c8bcd5ac4..8efb37d99e 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/testwasmlib.go @@ -137,3 +137,30 @@ func viewGetRandom(ctx wasmlib.ScViewContext, f *GetRandomContext) { func funcTriggerEvent(ctx wasmlib.ScFuncContext, f *TriggerEventContext) { f.Events.Test(f.Params.Address().Value(), f.Params.Name().Value()) } + +func funcMapClear(ctx wasmlib.ScFuncContext, f *MapClearContext) { + name := f.Params.Name().Value() + myMap := f.State.Maps().GetStringMap(name) + myMap.Clear() +} + +func funcMapCreate(ctx wasmlib.ScFuncContext, f *MapCreateContext) { + name := f.Params.Name().Value() + myMap := f.State.Maps().GetStringMap(name) + myMap.Clear() +} + +func funcMapSet(ctx wasmlib.ScFuncContext, f *MapSetContext) { + name := f.Params.Name().Value() + myMap := f.State.Maps().GetStringMap(name) + key := f.Params.Key().Value() + value := f.Params.Value().Value() + myMap.GetString(key).SetValue(value) +} + +func viewMapValue(ctx wasmlib.ScViewContext, f *MapValueContext) { + name := f.Params.Name().Value() + myMap := f.State.Maps().GetStringMap(name) + key := f.Params.Key().Value() + f.Results.Value().SetValue(myMap.GetString(key).Value()) +} diff --git a/contracts/wasm/testwasmlib/go/testwasmlib/typedefs.go b/contracts/wasm/testwasmlib/go/testwasmlib/typedefs.go index 28df5b3ef7..97090d3d58 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlib/typedefs.go +++ b/contracts/wasm/testwasmlib/go/testwasmlib/typedefs.go @@ -40,3 +40,27 @@ func (a ArrayOfMutableString) GetString(index int32) wasmlib.ScMutableString { } type MutableStringArray = ArrayOfMutableString + +type MapStringToImmutableString struct { + objID int32 +} + +func (m MapStringToImmutableString) GetString(key string) wasmlib.ScImmutableString { + return wasmlib.NewScImmutableString(m.objID, wasmlib.Key(key).KeyID()) +} + +type ImmutableStringMap = MapStringToImmutableString + +type MapStringToMutableString struct { + objID int32 +} + +func (m MapStringToMutableString) Clear() { + wasmlib.Clear(m.objID) +} + +func (m MapStringToMutableString) GetString(key string) wasmlib.ScMutableString { + return wasmlib.NewScMutableString(m.objID, wasmlib.Key(key).KeyID()) +} + +type MutableStringMap = MapStringToMutableString diff --git a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go index 1bb3838a12..60b4dbd9fb 100644 --- a/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go +++ b/contracts/wasm/testwasmlib/go/testwasmlibclient/service.go @@ -24,6 +24,7 @@ const ( ArgInt32 = "int32" ArgInt64 = "int64" ArgInt8 = "int8" + ArgKey = "key" ArgName = "name" ArgParam = "this" ArgRecordIndex = "recordIndex" @@ -101,6 +102,64 @@ func (f *ArraySetFunc) Post() wasmclient.Request { return f.ClientFunc.Post(0x2c4150b3, &f.args) } +///////////////////////////// mapClear ///////////////////////////// + +type MapClearFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *MapClearFunc) Name(v string) { + f.args.Set(ArgName, f.args.FromString(v)) +} + +func (f *MapClearFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgName) + return f.ClientFunc.Post(0x027f215a, &f.args) +} + +///////////////////////////// mapCreate ///////////////////////////// + +type MapCreateFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *MapCreateFunc) Name(v string) { + f.args.Set(ArgName, f.args.FromString(v)) +} + +func (f *MapCreateFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgName) + return f.ClientFunc.Post(0x6295d599, &f.args) +} + +///////////////////////////// mapSet ///////////////////////////// + +type MapSetFunc struct { + wasmclient.ClientFunc + args wasmclient.Arguments +} + +func (f *MapSetFunc) Key(v string) { + f.args.Set(ArgKey, f.args.FromString(v)) +} + +func (f *MapSetFunc) Name(v string) { + f.args.Set(ArgName, f.args.FromString(v)) +} + +func (f *MapSetFunc) Value(v string) { + f.args.Set(ArgValue, f.args.FromString(v)) +} + +func (f *MapSetFunc) Post() wasmclient.Request { + f.args.Mandatory(ArgKey) + f.args.Mandatory(ArgName) + f.args.Mandatory(ArgValue) + return f.ClientFunc.Post(0xf2260404, &f.args) +} + ///////////////////////////// paramTypes ///////////////////////////// type ParamTypesFunc struct { @@ -367,6 +426,36 @@ func (r *IotaBalanceResults) Iotas() int64 { return r.res.ToInt64(r.res.Get(ResIotas)) } +///////////////////////////// mapValue ///////////////////////////// + +type MapValueView struct { + wasmclient.ClientView + args wasmclient.Arguments +} + +func (f *MapValueView) Key(v string) { + f.args.Set(ArgKey, f.args.FromString(v)) +} + +func (f *MapValueView) Name(v string) { + f.args.Set(ArgName, f.args.FromString(v)) +} + +func (f *MapValueView) Call() MapValueResults { + f.args.Mandatory(ArgKey) + f.args.Mandatory(ArgName) + f.ClientView.Call("mapValue", &f.args) + return MapValueResults{res: f.Results()} +} + +type MapValueResults struct { + res wasmclient.Results +} + +func (r *MapValueResults) Value() string { + return r.res.ToString(r.res.Get(ResValue)) +} + ///////////////////////////// TestWasmLibService ///////////////////////////// type TestWasmLibService struct { @@ -391,6 +480,18 @@ func (s *TestWasmLibService) ArraySet() ArraySetFunc { return ArraySetFunc{ClientFunc: s.AsClientFunc()} } +func (s *TestWasmLibService) MapClear() MapClearFunc { + return MapClearFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *TestWasmLibService) MapCreate() MapCreateFunc { + return MapCreateFunc{ClientFunc: s.AsClientFunc()} +} + +func (s *TestWasmLibService) MapSet() MapSetFunc { + return MapSetFunc{ClientFunc: s.AsClientFunc()} +} + func (s *TestWasmLibService) ParamTypes() ParamTypesFunc { return ParamTypesFunc{ClientFunc: s.AsClientFunc()} } @@ -426,3 +527,7 @@ func (s *TestWasmLibService) GetRandom() GetRandomView { func (s *TestWasmLibService) IotaBalance() IotaBalanceView { return IotaBalanceView{ClientView: s.AsClientView()} } + +func (s *TestWasmLibService) MapValue() MapValueView { + return MapValueView{ClientView: s.AsClientView()} +} diff --git a/contracts/wasm/testwasmlib/schema.yaml b/contracts/wasm/testwasmlib/schema.yaml index 30f62287f1..7b1358e733 100644 --- a/contracts/wasm/testwasmlib/schema.yaml +++ b/contracts/wasm/testwasmlib/schema.yaml @@ -19,12 +19,14 @@ structs: typedefs: # ################################## StringArray: String[] + StringMap: map[String]String # Longitude: map[Int32]Location # ################################## state: # ################################## arrays: map[String]StringArray + maps: map[String]StringMap random: Int64 # latLong: map[Int32]Longitude @@ -45,6 +47,20 @@ funcs: name: String value: String + mapClear: + params: + name: String + + mapCreate: + params: + name: String + + mapSet: + params: + name: String + key: String + value: String + paramTypes: params: address: Address? @@ -107,6 +123,13 @@ views: results: iotas: Int64 + mapValue: + params: + name: String + key: String + results: + value: String + getRandom: results: random: Int64 diff --git a/contracts/wasm/testwasmlib/src/consts.rs b/contracts/wasm/testwasmlib/src/consts.rs index 31eec7ad2e..8842bfad04 100644 --- a/contracts/wasm/testwasmlib/src/consts.rs +++ b/contracts/wasm/testwasmlib/src/consts.rs @@ -27,6 +27,7 @@ pub const PARAM_INT16 : &str = "int16"; pub const PARAM_INT32 : &str = "int32"; pub const PARAM_INT64 : &str = "int64"; pub const PARAM_INT8 : &str = "int8"; +pub const PARAM_KEY : &str = "key"; pub const PARAM_NAME : &str = "name"; pub const PARAM_PARAM : &str = "this"; pub const PARAM_RECORD_INDEX : &str = "recordIndex"; @@ -46,11 +47,15 @@ pub const RESULT_RECORD : &str = "record"; pub const RESULT_VALUE : &str = "value"; pub const STATE_ARRAYS : &str = "arrays"; +pub const STATE_MAPS : &str = "maps"; pub const STATE_RANDOM : &str = "random"; pub const FUNC_ARRAY_CLEAR : &str = "arrayClear"; pub const FUNC_ARRAY_CREATE : &str = "arrayCreate"; pub const FUNC_ARRAY_SET : &str = "arraySet"; +pub const FUNC_MAP_CLEAR : &str = "mapClear"; +pub const FUNC_MAP_CREATE : &str = "mapCreate"; +pub const FUNC_MAP_SET : &str = "mapSet"; pub const FUNC_PARAM_TYPES : &str = "paramTypes"; pub const FUNC_RANDOM : &str = "random"; pub const FUNC_TRIGGER_EVENT : &str = "triggerEvent"; @@ -60,10 +65,14 @@ pub const VIEW_BLOCK_RECORD : &str = "blockRecord"; pub const VIEW_BLOCK_RECORDS : &str = "blockRecords"; pub const VIEW_GET_RANDOM : &str = "getRandom"; pub const VIEW_IOTA_BALANCE : &str = "iotaBalance"; +pub const VIEW_MAP_VALUE : &str = "mapValue"; pub const HFUNC_ARRAY_CLEAR : ScHname = ScHname(0x88021821); pub const HFUNC_ARRAY_CREATE : ScHname = ScHname(0x1ed5b23b); pub const HFUNC_ARRAY_SET : ScHname = ScHname(0x2c4150b3); +pub const HFUNC_MAP_CLEAR : ScHname = ScHname(0x027f215a); +pub const HFUNC_MAP_CREATE : ScHname = ScHname(0x6295d599); +pub const HFUNC_MAP_SET : ScHname = ScHname(0xf2260404); pub const HFUNC_PARAM_TYPES : ScHname = ScHname(0x6921c4cd); pub const HFUNC_RANDOM : ScHname = ScHname(0xe86c97ca); pub const HFUNC_TRIGGER_EVENT : ScHname = ScHname(0xd5438ac6); @@ -73,3 +82,4 @@ pub const HVIEW_BLOCK_RECORD : ScHname = ScHname(0xad13b2f8); pub const HVIEW_BLOCK_RECORDS : ScHname = ScHname(0x16e249ea); pub const HVIEW_GET_RANDOM : ScHname = ScHname(0x46263045); pub const HVIEW_IOTA_BALANCE : ScHname = ScHname(0x9d3920bd); +pub const HVIEW_MAP_VALUE : ScHname = ScHname(0x23149bef); diff --git a/contracts/wasm/testwasmlib/src/contract.rs b/contracts/wasm/testwasmlib/src/contract.rs index 9b7c6a7b43..8de723d684 100644 --- a/contracts/wasm/testwasmlib/src/contract.rs +++ b/contracts/wasm/testwasmlib/src/contract.rs @@ -30,6 +30,21 @@ pub struct ArraySetCall { pub params: MutableArraySetParams, } +pub struct MapClearCall { + pub func: ScFunc, + pub params: MutableMapClearParams, +} + +pub struct MapCreateCall { + pub func: ScFunc, + pub params: MutableMapCreateParams, +} + +pub struct MapSetCall { + pub func: ScFunc, + pub params: MutableMapSetParams, +} + pub struct ParamTypesCall { pub func: ScFunc, pub params: MutableParamTypesParams, @@ -78,6 +93,12 @@ pub struct IotaBalanceCall { pub results: ImmutableIotaBalanceResults, } +pub struct MapValueCall { + pub func: ScView, + pub params: MutableMapValueParams, + pub results: ImmutableMapValueResults, +} + pub struct ScFuncs { } @@ -109,6 +130,33 @@ impl ScFuncs { f } + pub fn map_clear(_ctx: & dyn ScFuncCallContext) -> MapClearCall { + let mut f = MapClearCall { + func: ScFunc::new(HSC_NAME, HFUNC_MAP_CLEAR), + params: MutableMapClearParams { id: 0 }, + }; + f.func.set_ptrs(&mut f.params.id, ptr::null_mut()); + f + } + + pub fn map_create(_ctx: & dyn ScFuncCallContext) -> MapCreateCall { + let mut f = MapCreateCall { + func: ScFunc::new(HSC_NAME, HFUNC_MAP_CREATE), + params: MutableMapCreateParams { id: 0 }, + }; + f.func.set_ptrs(&mut f.params.id, ptr::null_mut()); + f + } + + pub fn map_set(_ctx: & dyn ScFuncCallContext) -> MapSetCall { + let mut f = MapSetCall { + func: ScFunc::new(HSC_NAME, HFUNC_MAP_SET), + params: MutableMapSetParams { id: 0 }, + }; + f.func.set_ptrs(&mut f.params.id, ptr::null_mut()); + f + } + pub fn param_types(_ctx: & dyn ScFuncCallContext) -> ParamTypesCall { let mut f = ParamTypesCall { func: ScFunc::new(HSC_NAME, HFUNC_PARAM_TYPES), @@ -190,4 +238,14 @@ impl ScFuncs { f.func.set_ptrs(ptr::null_mut(), &mut f.results.id); f } + + pub fn map_value(_ctx: & dyn ScViewCallContext) -> MapValueCall { + let mut f = MapValueCall { + func: ScView::new(HSC_NAME, HVIEW_MAP_VALUE), + params: MutableMapValueParams { id: 0 }, + results: ImmutableMapValueResults { id: 0 }, + }; + f.func.set_ptrs(&mut f.params.id, &mut f.results.id); + f + } } diff --git a/contracts/wasm/testwasmlib/src/keys.rs b/contracts/wasm/testwasmlib/src/keys.rs index f20b5ce4ed..7aeff86e99 100644 --- a/contracts/wasm/testwasmlib/src/keys.rs +++ b/contracts/wasm/testwasmlib/src/keys.rs @@ -25,28 +25,30 @@ pub(crate) const IDX_PARAM_INT16 : usize = 10; pub(crate) const IDX_PARAM_INT32 : usize = 11; pub(crate) const IDX_PARAM_INT64 : usize = 12; pub(crate) const IDX_PARAM_INT8 : usize = 13; -pub(crate) const IDX_PARAM_NAME : usize = 14; -pub(crate) const IDX_PARAM_PARAM : usize = 15; -pub(crate) const IDX_PARAM_RECORD_INDEX : usize = 16; -pub(crate) const IDX_PARAM_REQUEST_ID : usize = 17; -pub(crate) const IDX_PARAM_STRING : usize = 18; -pub(crate) const IDX_PARAM_UINT16 : usize = 19; -pub(crate) const IDX_PARAM_UINT32 : usize = 20; -pub(crate) const IDX_PARAM_UINT64 : usize = 21; -pub(crate) const IDX_PARAM_UINT8 : usize = 22; -pub(crate) const IDX_PARAM_VALUE : usize = 23; +pub(crate) const IDX_PARAM_KEY : usize = 14; +pub(crate) const IDX_PARAM_NAME : usize = 15; +pub(crate) const IDX_PARAM_PARAM : usize = 16; +pub(crate) const IDX_PARAM_RECORD_INDEX : usize = 17; +pub(crate) const IDX_PARAM_REQUEST_ID : usize = 18; +pub(crate) const IDX_PARAM_STRING : usize = 19; +pub(crate) const IDX_PARAM_UINT16 : usize = 20; +pub(crate) const IDX_PARAM_UINT32 : usize = 21; +pub(crate) const IDX_PARAM_UINT64 : usize = 22; +pub(crate) const IDX_PARAM_UINT8 : usize = 23; +pub(crate) const IDX_PARAM_VALUE : usize = 24; -pub(crate) const IDX_RESULT_COUNT : usize = 24; -pub(crate) const IDX_RESULT_IOTAS : usize = 25; -pub(crate) const IDX_RESULT_LENGTH : usize = 26; -pub(crate) const IDX_RESULT_RANDOM : usize = 27; -pub(crate) const IDX_RESULT_RECORD : usize = 28; -pub(crate) const IDX_RESULT_VALUE : usize = 29; +pub(crate) const IDX_RESULT_COUNT : usize = 25; +pub(crate) const IDX_RESULT_IOTAS : usize = 26; +pub(crate) const IDX_RESULT_LENGTH : usize = 27; +pub(crate) const IDX_RESULT_RANDOM : usize = 28; +pub(crate) const IDX_RESULT_RECORD : usize = 29; +pub(crate) const IDX_RESULT_VALUE : usize = 30; -pub(crate) const IDX_STATE_ARRAYS : usize = 30; -pub(crate) const IDX_STATE_RANDOM : usize = 31; +pub(crate) const IDX_STATE_ARRAYS : usize = 31; +pub(crate) const IDX_STATE_MAPS : usize = 32; +pub(crate) const IDX_STATE_RANDOM : usize = 33; -pub const KEY_MAP_LEN: usize = 32; +pub const KEY_MAP_LEN: usize = 34; pub const KEY_MAP: [&str; KEY_MAP_LEN] = [ PARAM_ADDRESS, @@ -63,6 +65,7 @@ pub const KEY_MAP: [&str; KEY_MAP_LEN] = [ PARAM_INT32, PARAM_INT64, PARAM_INT8, + PARAM_KEY, PARAM_NAME, PARAM_PARAM, PARAM_RECORD_INDEX, @@ -80,6 +83,7 @@ pub const KEY_MAP: [&str; KEY_MAP_LEN] = [ RESULT_RECORD, RESULT_VALUE, STATE_ARRAYS, + STATE_MAPS, STATE_RANDOM, ]; diff --git a/contracts/wasm/testwasmlib/src/lib.rs b/contracts/wasm/testwasmlib/src/lib.rs index 84ecf72916..b5f7cade6a 100644 --- a/contracts/wasm/testwasmlib/src/lib.rs +++ b/contracts/wasm/testwasmlib/src/lib.rs @@ -35,6 +35,9 @@ fn on_load() { exports.add_func(FUNC_ARRAY_CLEAR, func_array_clear_thunk); exports.add_func(FUNC_ARRAY_CREATE, func_array_create_thunk); exports.add_func(FUNC_ARRAY_SET, func_array_set_thunk); + exports.add_func(FUNC_MAP_CLEAR, func_map_clear_thunk); + exports.add_func(FUNC_MAP_CREATE, func_map_create_thunk); + exports.add_func(FUNC_MAP_SET, func_map_set_thunk); exports.add_func(FUNC_PARAM_TYPES, func_param_types_thunk); exports.add_func(FUNC_RANDOM, func_random_thunk); exports.add_func(FUNC_TRIGGER_EVENT, func_trigger_event_thunk); @@ -44,6 +47,7 @@ fn on_load() { exports.add_view(VIEW_BLOCK_RECORDS, view_block_records_thunk); exports.add_view(VIEW_GET_RANDOM, view_get_random_thunk); exports.add_view(VIEW_IOTA_BALANCE, view_iota_balance_thunk); + exports.add_view(VIEW_MAP_VALUE, view_map_value_thunk); unsafe { for i in 0..KEY_MAP_LEN { @@ -120,6 +124,74 @@ fn func_array_set_thunk(ctx: &ScFuncContext) { ctx.log("testwasmlib.funcArraySet ok"); } +pub struct MapClearContext { + events: TestWasmLibEvents, + params: ImmutableMapClearParams, + state: MutableTestWasmLibState, +} + +fn func_map_clear_thunk(ctx: &ScFuncContext) { + ctx.log("testwasmlib.funcMapClear"); + let f = MapClearContext { + events: TestWasmLibEvents {}, + params: ImmutableMapClearParams { + id: OBJ_ID_PARAMS, + }, + state: MutableTestWasmLibState { + id: OBJ_ID_STATE, + }, + }; + ctx.require(f.params.name().exists(), "missing mandatory name"); + func_map_clear(ctx, &f); + ctx.log("testwasmlib.funcMapClear ok"); +} + +pub struct MapCreateContext { + events: TestWasmLibEvents, + params: ImmutableMapCreateParams, + state: MutableTestWasmLibState, +} + +fn func_map_create_thunk(ctx: &ScFuncContext) { + ctx.log("testwasmlib.funcMapCreate"); + let f = MapCreateContext { + events: TestWasmLibEvents {}, + params: ImmutableMapCreateParams { + id: OBJ_ID_PARAMS, + }, + state: MutableTestWasmLibState { + id: OBJ_ID_STATE, + }, + }; + ctx.require(f.params.name().exists(), "missing mandatory name"); + func_map_create(ctx, &f); + ctx.log("testwasmlib.funcMapCreate ok"); +} + +pub struct MapSetContext { + events: TestWasmLibEvents, + params: ImmutableMapSetParams, + state: MutableTestWasmLibState, +} + +fn func_map_set_thunk(ctx: &ScFuncContext) { + ctx.log("testwasmlib.funcMapSet"); + let f = MapSetContext { + events: TestWasmLibEvents {}, + params: ImmutableMapSetParams { + id: OBJ_ID_PARAMS, + }, + state: MutableTestWasmLibState { + id: OBJ_ID_STATE, + }, + }; + ctx.require(f.params.key().exists(), "missing mandatory key"); + ctx.require(f.params.name().exists(), "missing mandatory name"); + ctx.require(f.params.value().exists(), "missing mandatory value"); + func_map_set(ctx, &f); + ctx.log("testwasmlib.funcMapSet ok"); +} + pub struct ParamTypesContext { events: TestWasmLibEvents, params: ImmutableParamTypesParams, @@ -316,3 +388,28 @@ fn view_iota_balance_thunk(ctx: &ScViewContext) { view_iota_balance(ctx, &f); ctx.log("testwasmlib.viewIotaBalance ok"); } + +pub struct MapValueContext { + params: ImmutableMapValueParams, + results: MutableMapValueResults, + state: ImmutableTestWasmLibState, +} + +fn view_map_value_thunk(ctx: &ScViewContext) { + ctx.log("testwasmlib.viewMapValue"); + let f = MapValueContext { + params: ImmutableMapValueParams { + id: OBJ_ID_PARAMS, + }, + results: MutableMapValueResults { + id: OBJ_ID_RESULTS, + }, + state: ImmutableTestWasmLibState { + id: OBJ_ID_STATE, + }, + }; + ctx.require(f.params.key().exists(), "missing mandatory key"); + ctx.require(f.params.name().exists(), "missing mandatory name"); + view_map_value(ctx, &f); + ctx.log("testwasmlib.viewMapValue ok"); +} diff --git a/contracts/wasm/testwasmlib/src/params.rs b/contracts/wasm/testwasmlib/src/params.rs index e092b52390..1d9f4e9d87 100644 --- a/contracts/wasm/testwasmlib/src/params.rs +++ b/contracts/wasm/testwasmlib/src/params.rs @@ -97,6 +97,88 @@ impl MutableArraySetParams { } } +#[derive(Clone, Copy)] +pub struct ImmutableMapClearParams { + pub(crate) id: i32, +} + +impl ImmutableMapClearParams { + pub fn name(&self) -> ScImmutableString { + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) + } +} + +#[derive(Clone, Copy)] +pub struct MutableMapClearParams { + pub(crate) id: i32, +} + +impl MutableMapClearParams { + pub fn name(&self) -> ScMutableString { + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) + } +} + +#[derive(Clone, Copy)] +pub struct ImmutableMapCreateParams { + pub(crate) id: i32, +} + +impl ImmutableMapCreateParams { + pub fn name(&self) -> ScImmutableString { + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) + } +} + +#[derive(Clone, Copy)] +pub struct MutableMapCreateParams { + pub(crate) id: i32, +} + +impl MutableMapCreateParams { + pub fn name(&self) -> ScMutableString { + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) + } +} + +#[derive(Clone, Copy)] +pub struct ImmutableMapSetParams { + pub(crate) id: i32, +} + +impl ImmutableMapSetParams { + pub fn key(&self) -> ScImmutableString { + ScImmutableString::new(self.id, PARAM_KEY.get_key_id()) + } + + pub fn name(&self) -> ScImmutableString { + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) + } + + pub fn value(&self) -> ScImmutableString { + ScImmutableString::new(self.id, PARAM_VALUE.get_key_id()) + } +} + +#[derive(Clone, Copy)] +pub struct MutableMapSetParams { + pub(crate) id: i32, +} + +impl MutableMapSetParams { + pub fn key(&self) -> ScMutableString { + ScMutableString::new(self.id, PARAM_KEY.get_key_id()) + } + + pub fn name(&self) -> ScMutableString { + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) + } + + pub fn value(&self) -> ScMutableString { + ScMutableString::new(self.id, PARAM_VALUE.get_key_id()) + } +} + #[derive(Clone, Copy)] pub struct MapStringToImmutableBytes { pub(crate) obj_id: i32, @@ -422,3 +504,33 @@ impl MutableBlockRecordsParams { ScMutableInt32::new(self.id, PARAM_BLOCK_INDEX.get_key_id()) } } + +#[derive(Clone, Copy)] +pub struct ImmutableMapValueParams { + pub(crate) id: i32, +} + +impl ImmutableMapValueParams { + pub fn key(&self) -> ScImmutableString { + ScImmutableString::new(self.id, PARAM_KEY.get_key_id()) + } + + pub fn name(&self) -> ScImmutableString { + ScImmutableString::new(self.id, PARAM_NAME.get_key_id()) + } +} + +#[derive(Clone, Copy)] +pub struct MutableMapValueParams { + pub(crate) id: i32, +} + +impl MutableMapValueParams { + pub fn key(&self) -> ScMutableString { + ScMutableString::new(self.id, PARAM_KEY.get_key_id()) + } + + pub fn name(&self) -> ScMutableString { + ScMutableString::new(self.id, PARAM_NAME.get_key_id()) + } +} diff --git a/contracts/wasm/testwasmlib/src/results.rs b/contracts/wasm/testwasmlib/src/results.rs index 6b285e9068..e46d27f60b 100644 --- a/contracts/wasm/testwasmlib/src/results.rs +++ b/contracts/wasm/testwasmlib/src/results.rs @@ -146,3 +146,25 @@ impl MutableIotaBalanceResults { ScMutableInt64::new(self.id, RESULT_IOTAS.get_key_id()) } } + +#[derive(Clone, Copy)] +pub struct ImmutableMapValueResults { + pub(crate) id: i32, +} + +impl ImmutableMapValueResults { + pub fn value(&self) -> ScImmutableString { + ScImmutableString::new(self.id, RESULT_VALUE.get_key_id()) + } +} + +#[derive(Clone, Copy)] +pub struct MutableMapValueResults { + pub(crate) id: i32, +} + +impl MutableMapValueResults { + pub fn value(&self) -> ScMutableString { + ScMutableString::new(self.id, RESULT_VALUE.get_key_id()) + } +} diff --git a/contracts/wasm/testwasmlib/src/state.rs b/contracts/wasm/testwasmlib/src/state.rs index 0cf46c6c3e..01ceb24c3b 100644 --- a/contracts/wasm/testwasmlib/src/state.rs +++ b/contracts/wasm/testwasmlib/src/state.rs @@ -27,6 +27,18 @@ impl MapStringToImmutableStringArray { } } +#[derive(Clone, Copy)] +pub struct MapStringToImmutableStringMap { + pub(crate) obj_id: i32, +} + +impl MapStringToImmutableStringMap { + pub fn get_string_map(&self, key: &str) -> ImmutableStringMap { + let sub_id = get_object_id(self.obj_id, key.get_key_id(), TYPE_MAP); + ImmutableStringMap { obj_id: sub_id } + } +} + #[derive(Clone, Copy)] pub struct ImmutableTestWasmLibState { pub(crate) id: i32, @@ -38,6 +50,11 @@ impl ImmutableTestWasmLibState { MapStringToImmutableStringArray { obj_id: map_id } } + pub fn maps(&self) -> MapStringToImmutableStringMap { + let map_id = get_object_id(self.id, STATE_MAPS.get_key_id(), TYPE_MAP); + MapStringToImmutableStringMap { obj_id: map_id } + } + pub fn random(&self) -> ScImmutableInt64 { ScImmutableInt64::new(self.id, STATE_RANDOM.get_key_id()) } @@ -59,6 +76,22 @@ impl MapStringToMutableStringArray { } } +#[derive(Clone, Copy)] +pub struct MapStringToMutableStringMap { + pub(crate) obj_id: i32, +} + +impl MapStringToMutableStringMap { + pub fn clear(&self) { + clear(self.obj_id); + } + + pub fn get_string_map(&self, key: &str) -> MutableStringMap { + let sub_id = get_object_id(self.obj_id, key.get_key_id(), TYPE_MAP); + MutableStringMap { obj_id: sub_id } + } +} + #[derive(Clone, Copy)] pub struct MutableTestWasmLibState { pub(crate) id: i32, @@ -74,6 +107,11 @@ impl MutableTestWasmLibState { MapStringToMutableStringArray { obj_id: map_id } } + pub fn maps(&self) -> MapStringToMutableStringMap { + let map_id = get_object_id(self.id, STATE_MAPS.get_key_id(), TYPE_MAP); + MapStringToMutableStringMap { obj_id: map_id } + } + pub fn random(&self) -> ScMutableInt64 { ScMutableInt64::new(self.id, STATE_RANDOM.get_key_id()) } diff --git a/contracts/wasm/testwasmlib/src/testwasmlib.rs b/contracts/wasm/testwasmlib/src/testwasmlib.rs index c8f3243b87..31db1e8ab6 100644 --- a/contracts/wasm/testwasmlib/src/testwasmlib.rs +++ b/contracts/wasm/testwasmlib/src/testwasmlib.rs @@ -132,3 +132,15 @@ pub fn view_get_random(_ctx: &ScViewContext, f: &GetRandomContext) { pub fn func_trigger_event(_ctx: &ScFuncContext, f: &TriggerEventContext) { f.events.test(&f.params.address().value(), &f.params.name().value()); } + +pub fn func_map_clear(_ctx: &ScFuncContext, _f: &MapClearContext) { +} + +pub fn func_map_create(_ctx: &ScFuncContext, _f: &MapCreateContext) { +} + +pub fn func_map_set(_ctx: &ScFuncContext, _f: &MapSetContext) { +} + +pub fn view_map_value(_ctx: &ScViewContext, _f: &MapValueContext) { +} diff --git a/contracts/wasm/testwasmlib/src/typedefs.rs b/contracts/wasm/testwasmlib/src/typedefs.rs index 5a61439653..7f96eb2368 100644 --- a/contracts/wasm/testwasmlib/src/typedefs.rs +++ b/contracts/wasm/testwasmlib/src/typedefs.rs @@ -47,3 +47,33 @@ impl ArrayOfMutableString { } pub type MutableStringArray = ArrayOfMutableString; + +#[derive(Clone, Copy)] +pub struct MapStringToImmutableString { + pub(crate) obj_id: i32, +} + +impl MapStringToImmutableString { + pub fn get_string(&self, key: &str) -> ScImmutableString { + ScImmutableString::new(self.obj_id, key.get_key_id()) + } +} + +pub type ImmutableStringMap = MapStringToImmutableString; + +#[derive(Clone, Copy)] +pub struct MapStringToMutableString { + pub(crate) obj_id: i32, +} + +impl MapStringToMutableString { + pub fn clear(&self) { + clear(self.obj_id); + } + + pub fn get_string(&self, key: &str) -> ScMutableString { + ScMutableString::new(self.obj_id, key.get_key_id()) + } +} + +pub type MutableStringMap = MapStringToMutableString; diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm b/contracts/wasm/testwasmlib/test/testwasmlib_bg.wasm index 139f4a2b43e3681430d61b7769a9c2fa2335cbfc..39e10ae7242ee578d24ecbaf4232e0779f9347d1 100644 GIT binary patch delta 16986 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 delta 16019 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 z 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 YPon;lcW0OZifIDOqjLARzrW)D0^h}-)c^nh diff --git a/contracts/wasm/testwasmlib/test/testwasmlib_test.go b/contracts/wasm/testwasmlib/test/testwasmlib_test.go index 3cc625fccc..1fb5acb218 100644 --- a/contracts/wasm/testwasmlib/test/testwasmlib_test.go +++ b/contracts/wasm/testwasmlib/test/testwasmlib_test.go @@ -245,6 +245,56 @@ func TestClearArray(t *testing.T) { require.Error(t, ctx.Err) } +func TestClearMap(t *testing.T) { + // test reproduces a problem that needs fixing + t.SkipNow() + + *wasmsolo.GoDebug = true + ctx := setupTest(t) + + as := testwasmlib.ScFuncs.MapSet(ctx) + as.Params.Name().SetValue("albums") + as.Params.Key().SetValue("Simple Minds") + as.Params.Value().SetValue("New Gold Dream") + as.Func.TransferIotas(1).Post() + require.NoError(t, ctx.Err) + + as = testwasmlib.ScFuncs.MapSet(ctx) + as.Params.Name().SetValue("bands") + as.Params.Key().SetValue("Dire Straits") + as.Params.Value().SetValue("Calling Elvis") + as.Func.TransferIotas(1).Post() + require.NoError(t, ctx.Err) + + as = testwasmlib.ScFuncs.MapSet(ctx) + as.Params.Name().SetValue("bands") + as.Params.Key().SetValue("ELO") + as.Params.Value().SetValue("Mr. Blue Sky") + as.Func.TransferIotas(1).Post() + require.NoError(t, ctx.Err) + + av := testwasmlib.ScFuncs.MapValue(ctx) + av.Params.Name().SetValue("bands") + av.Params.Key().SetValue("Dire Straits") + av.Func.Call() + require.NoError(t, ctx.Err) + value := av.Results.Value() + require.True(t, value.Exists()) + require.EqualValues(t, "Calling Elvis", value.Value()) + + ac := testwasmlib.ScFuncs.ArrayClear(ctx) + ac.Params.Name().SetValue("bands") + ac.Func.TransferIotas(1).Post() + require.NoError(t, ctx.Err) + + av = testwasmlib.ScFuncs.MapValue(ctx) + av.Params.Name().SetValue("bands") + av.Params.Key().SetValue("Dire Straits") + av.Func.Call() + require.NoError(t, ctx.Err) + require.EqualValues(t, "", value.Value()) +} + func TestViewBalance(t *testing.T) { ctx := setupTest(t) diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts index daf3f5ed6a..fbce884289 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/consts.ts @@ -25,6 +25,7 @@ export const ParamInt16 = "int16"; export const ParamInt32 = "int32"; export const ParamInt64 = "int64"; export const ParamInt8 = "int8"; +export const ParamKey = "key"; export const ParamName = "name"; export const ParamParam = "this"; export const ParamRecordIndex = "recordIndex"; @@ -44,11 +45,15 @@ export const ResultRecord = "record"; export const ResultValue = "value"; export const StateArrays = "arrays"; +export const StateMaps = "maps"; export const StateRandom = "random"; export const FuncArrayClear = "arrayClear"; export const FuncArrayCreate = "arrayCreate"; export const FuncArraySet = "arraySet"; +export const FuncMapClear = "mapClear"; +export const FuncMapCreate = "mapCreate"; +export const FuncMapSet = "mapSet"; export const FuncParamTypes = "paramTypes"; export const FuncRandom = "random"; export const FuncTriggerEvent = "triggerEvent"; @@ -58,10 +63,14 @@ export const ViewBlockRecord = "blockRecord"; export const ViewBlockRecords = "blockRecords"; export const ViewGetRandom = "getRandom"; export const ViewIotaBalance = "iotaBalance"; +export const ViewMapValue = "mapValue"; export const HFuncArrayClear = new wasmlib.ScHname(0x88021821); export const HFuncArrayCreate = new wasmlib.ScHname(0x1ed5b23b); export const HFuncArraySet = new wasmlib.ScHname(0x2c4150b3); +export const HFuncMapClear = new wasmlib.ScHname(0x027f215a); +export const HFuncMapCreate = new wasmlib.ScHname(0x6295d599); +export const HFuncMapSet = new wasmlib.ScHname(0xf2260404); export const HFuncParamTypes = new wasmlib.ScHname(0x6921c4cd); export const HFuncRandom = new wasmlib.ScHname(0xe86c97ca); export const HFuncTriggerEvent = new wasmlib.ScHname(0xd5438ac6); @@ -71,3 +80,4 @@ export const HViewBlockRecord = new wasmlib.ScHname(0xad13b2f8); export const HViewBlockRecords = new wasmlib.ScHname(0x16e249ea); export const HViewGetRandom = new wasmlib.ScHname(0x46263045); export const HViewIotaBalance = new wasmlib.ScHname(0x9d3920bd); +export const HViewMapValue = new wasmlib.ScHname(0x23149bef); diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts index f6456b1007..0b3ecd8c70 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/contract.ts @@ -14,7 +14,7 @@ export class ArrayClearCall { } export class ArrayClearContext { - events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableArrayClearParams = new sc.ImmutableArrayClearParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -25,7 +25,7 @@ export class ArrayCreateCall { } export class ArrayCreateContext { - events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableArrayCreateParams = new sc.ImmutableArrayCreateParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -36,18 +36,51 @@ export class ArraySetCall { } export class ArraySetContext { - events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableArraySetParams = new sc.ImmutableArraySetParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } +export class MapClearCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncMapClear); + params: sc.MutableMapClearParams = new sc.MutableMapClearParams(); +} + +export class MapClearContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + params: sc.ImmutableMapClearParams = new sc.ImmutableMapClearParams(); + state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); +} + +export class MapCreateCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncMapCreate); + params: sc.MutableMapCreateParams = new sc.MutableMapCreateParams(); +} + +export class MapCreateContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + params: sc.ImmutableMapCreateParams = new sc.ImmutableMapCreateParams(); + state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); +} + +export class MapSetCall { + func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncMapSet); + params: sc.MutableMapSetParams = new sc.MutableMapSetParams(); +} + +export class MapSetContext { + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + params: sc.ImmutableMapSetParams = new sc.ImmutableMapSetParams(); + state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); +} + export class ParamTypesCall { func: wasmlib.ScFunc = new wasmlib.ScFunc(sc.HScName, sc.HFuncParamTypes); params: sc.MutableParamTypesParams = new sc.MutableParamTypesParams(); } export class ParamTypesContext { - events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableParamTypesParams = new sc.ImmutableParamTypesParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -57,7 +90,7 @@ export class RandomCall { } export class RandomContext { - events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -67,7 +100,7 @@ export class TriggerEventCall { } export class TriggerEventContext { - events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); + events: sc.TestWasmLibEvents = new sc.TestWasmLibEvents(); params: sc.ImmutableTriggerEventParams = new sc.ImmutableTriggerEventParams(); state: sc.MutableTestWasmLibState = new sc.MutableTestWasmLibState(); } @@ -140,6 +173,18 @@ export class IotaBalanceContext { state: sc.ImmutableTestWasmLibState = new sc.ImmutableTestWasmLibState(); } +export class MapValueCall { + func: wasmlib.ScView = new wasmlib.ScView(sc.HScName, sc.HViewMapValue); + params: sc.MutableMapValueParams = new sc.MutableMapValueParams(); + results: sc.ImmutableMapValueResults = new sc.ImmutableMapValueResults(); +} + +export class MapValueContext { + params: sc.ImmutableMapValueParams = new sc.ImmutableMapValueParams(); + results: sc.MutableMapValueResults = new sc.MutableMapValueResults(); + state: sc.ImmutableTestWasmLibState = new sc.ImmutableTestWasmLibState(); +} + export class ScFuncs { static arrayClear(ctx: wasmlib.ScFuncCallContext): ArrayClearCall { let f = new ArrayClearCall(); @@ -159,6 +204,24 @@ export class ScFuncs { return f; } + static mapClear(ctx: wasmlib.ScFuncCallContext): MapClearCall { + let f = new MapClearCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static mapCreate(ctx: wasmlib.ScFuncCallContext): MapCreateCall { + let f = new MapCreateCall(); + f.func.setPtrs(f.params, null); + return f; + } + + static mapSet(ctx: wasmlib.ScFuncCallContext): MapSetCall { + let f = new MapSetCall(); + f.func.setPtrs(f.params, null); + return f; + } + static paramTypes(ctx: wasmlib.ScFuncCallContext): ParamTypesCall { let f = new ParamTypesCall(); f.func.setPtrs(f.params, null); @@ -210,4 +273,10 @@ export class ScFuncs { f.func.setPtrs(null, f.results); return f; } + + static mapValue(ctx: wasmlib.ScViewCallContext): MapValueCall { + let f = new MapValueCall(); + f.func.setPtrs(f.params, f.results); + return f; + } } diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/keys.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/keys.ts index a67473b453..92517c4880 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/keys.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/keys.ts @@ -22,26 +22,28 @@ export const IdxParamInt16 = 10; export const IdxParamInt32 = 11; export const IdxParamInt64 = 12; export const IdxParamInt8 = 13; -export const IdxParamName = 14; -export const IdxParamParam = 15; -export const IdxParamRecordIndex = 16; -export const IdxParamRequestID = 17; -export const IdxParamString = 18; -export const IdxParamUint16 = 19; -export const IdxParamUint32 = 20; -export const IdxParamUint64 = 21; -export const IdxParamUint8 = 22; -export const IdxParamValue = 23; +export const IdxParamKey = 14; +export const IdxParamName = 15; +export const IdxParamParam = 16; +export const IdxParamRecordIndex = 17; +export const IdxParamRequestID = 18; +export const IdxParamString = 19; +export const IdxParamUint16 = 20; +export const IdxParamUint32 = 21; +export const IdxParamUint64 = 22; +export const IdxParamUint8 = 23; +export const IdxParamValue = 24; -export const IdxResultCount = 24; -export const IdxResultIotas = 25; -export const IdxResultLength = 26; -export const IdxResultRandom = 27; -export const IdxResultRecord = 28; -export const IdxResultValue = 29; +export const IdxResultCount = 25; +export const IdxResultIotas = 26; +export const IdxResultLength = 27; +export const IdxResultRandom = 28; +export const IdxResultRecord = 29; +export const IdxResultValue = 30; -export const IdxStateArrays = 30; -export const IdxStateRandom = 31; +export const IdxStateArrays = 31; +export const IdxStateMaps = 32; +export const IdxStateRandom = 33; export let keyMap: string[] = [ sc.ParamAddress, @@ -58,6 +60,7 @@ export let keyMap: string[] = [ sc.ParamInt32, sc.ParamInt64, sc.ParamInt8, + sc.ParamKey, sc.ParamName, sc.ParamParam, sc.ParamRecordIndex, @@ -75,6 +78,7 @@ export let keyMap: string[] = [ sc.ResultRecord, sc.ResultValue, sc.StateArrays, + sc.StateMaps, sc.StateRandom, ]; diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts index d5563333a3..10066f63d0 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/lib.ts @@ -17,6 +17,9 @@ export function on_load(): void { exports.addFunc(sc.FuncArrayClear, funcArrayClearThunk); exports.addFunc(sc.FuncArrayCreate, funcArrayCreateThunk); exports.addFunc(sc.FuncArraySet, funcArraySetThunk); + exports.addFunc(sc.FuncMapClear, funcMapClearThunk); + exports.addFunc(sc.FuncMapCreate, funcMapCreateThunk); + exports.addFunc(sc.FuncMapSet, funcMapSetThunk); exports.addFunc(sc.FuncParamTypes, funcParamTypesThunk); exports.addFunc(sc.FuncRandom, funcRandomThunk); exports.addFunc(sc.FuncTriggerEvent, funcTriggerEventThunk); @@ -26,6 +29,7 @@ export function on_load(): void { exports.addView(sc.ViewBlockRecords, viewBlockRecordsThunk); exports.addView(sc.ViewGetRandom, viewGetRandomThunk); exports.addView(sc.ViewIotaBalance, viewIotaBalanceThunk); + exports.addView(sc.ViewMapValue, viewMapValueThunk); for (let i = 0; i < sc.keyMap.length; i++) { sc.idxMap[i] = wasmlib.Key32.fromString(sc.keyMap[i]); @@ -64,6 +68,38 @@ function funcArraySetThunk(ctx: wasmlib.ScFuncContext): void { ctx.log("testwasmlib.funcArraySet ok"); } +function funcMapClearThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testwasmlib.funcMapClear"); + let f = new sc.MapClearContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.name().exists(), "missing mandatory name"); + sc.funcMapClear(ctx, f); + ctx.log("testwasmlib.funcMapClear ok"); +} + +function funcMapCreateThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testwasmlib.funcMapCreate"); + let f = new sc.MapCreateContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.name().exists(), "missing mandatory name"); + sc.funcMapCreate(ctx, f); + ctx.log("testwasmlib.funcMapCreate ok"); +} + +function funcMapSetThunk(ctx: wasmlib.ScFuncContext): void { + ctx.log("testwasmlib.funcMapSet"); + let f = new sc.MapSetContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.key().exists(), "missing mandatory key"); + ctx.require(f.params.name().exists(), "missing mandatory name"); + ctx.require(f.params.value().exists(), "missing mandatory value"); + sc.funcMapSet(ctx, f); + ctx.log("testwasmlib.funcMapSet ok"); +} + function funcParamTypesThunk(ctx: wasmlib.ScFuncContext): void { ctx.log("testwasmlib.funcParamTypes"); let f = new sc.ParamTypesContext(); @@ -155,3 +191,15 @@ function viewIotaBalanceThunk(ctx: wasmlib.ScViewContext): void { sc.viewIotaBalance(ctx, f); ctx.log("testwasmlib.viewIotaBalance ok"); } + +function viewMapValueThunk(ctx: wasmlib.ScViewContext): void { + ctx.log("testwasmlib.viewMapValue"); + let f = new sc.MapValueContext(); + f.params.mapID = wasmlib.OBJ_ID_PARAMS; + f.results.mapID = wasmlib.OBJ_ID_RESULTS; + f.state.mapID = wasmlib.OBJ_ID_STATE; + ctx.require(f.params.key().exists(), "missing mandatory key"); + ctx.require(f.params.name().exists(), "missing mandatory name"); + sc.viewMapValue(ctx, f); + ctx.log("testwasmlib.viewMapValue ok"); +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts index bce9fa1144..cacc72ca4d 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/params.ts @@ -60,6 +60,58 @@ export class MutableArraySetParams extends wasmlib.ScMapID { } } +export class ImmutableMapClearParams extends wasmlib.ScMapID { + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } +} + +export class MutableMapClearParams extends wasmlib.ScMapID { + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } +} + +export class ImmutableMapCreateParams extends wasmlib.ScMapID { + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } +} + +export class MutableMapCreateParams extends wasmlib.ScMapID { + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } +} + +export class ImmutableMapSetParams extends wasmlib.ScMapID { + key(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamKey)); + } + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } + + value(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamValue)); + } +} + +export class MutableMapSetParams extends wasmlib.ScMapID { + key(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamKey)); + } + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } + + value(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamValue)); + } +} + export class MapStringToImmutableBytes { objID: i32; @@ -327,3 +379,23 @@ export class MutableBlockRecordsParams extends wasmlib.ScMapID { return new wasmlib.ScMutableInt32(this.mapID, wasmlib.Key32.fromString(sc.ParamBlockIndex)); } } + +export class ImmutableMapValueParams extends wasmlib.ScMapID { + key(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamKey)); + } + + name(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } +} + +export class MutableMapValueParams extends wasmlib.ScMapID { + key(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamKey)); + } + + name(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ParamName)); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/results.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/results.ts index 5f3f467f24..476649ffcd 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/results.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/results.ts @@ -79,3 +79,15 @@ export class MutableIotaBalanceResults extends wasmlib.ScMapID { return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.ResultIotas)); } } + +export class ImmutableMapValueResults extends wasmlib.ScMapID { + value(): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultValue)); + } +} + +export class MutableMapValueResults extends wasmlib.ScMapID { + value(): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.mapID, wasmlib.Key32.fromString(sc.ResultValue)); + } +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts index 4d9d827c70..bdc4b782a3 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/state.ts @@ -21,12 +21,30 @@ export class MapStringToImmutableStringArray { } } +export class MapStringToImmutableStringMap { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getStringMap(key: string): sc.ImmutableStringMap { + let subID = wasmlib.getObjectID(this.objID, wasmlib.Key32.fromString(key), wasmlib.TYPE_MAP); + return new sc.ImmutableStringMap(subID); + } +} + export class ImmutableTestWasmLibState extends wasmlib.ScMapID { arrays(): sc.MapStringToImmutableStringArray { let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateArrays), wasmlib.TYPE_MAP); return new sc.MapStringToImmutableStringArray(mapID); } + maps(): sc.MapStringToImmutableStringMap { + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateMaps), wasmlib.TYPE_MAP); + return new sc.MapStringToImmutableStringMap(mapID); + } + random(): wasmlib.ScImmutableInt64 { return new wasmlib.ScImmutableInt64(this.mapID, wasmlib.Key32.fromString(sc.StateRandom)); } @@ -49,6 +67,23 @@ export class MapStringToMutableStringArray { } } +export class MapStringToMutableStringMap { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + getStringMap(key: string): sc.MutableStringMap { + let subID = wasmlib.getObjectID(this.objID, wasmlib.Key32.fromString(key), wasmlib.TYPE_MAP); + return new sc.MutableStringMap(subID); + } +} + export class MutableTestWasmLibState extends wasmlib.ScMapID { asImmutable(): sc.ImmutableTestWasmLibState { const imm = new sc.ImmutableTestWasmLibState(); @@ -61,6 +96,11 @@ export class MutableTestWasmLibState extends wasmlib.ScMapID { return new sc.MapStringToMutableStringArray(mapID); } + maps(): sc.MapStringToMutableStringMap { + let mapID = wasmlib.getObjectID(this.mapID, wasmlib.Key32.fromString(sc.StateMaps), wasmlib.TYPE_MAP); + return new sc.MapStringToMutableStringMap(mapID); + } + random(): wasmlib.ScMutableInt64 { return new wasmlib.ScMutableInt64(this.mapID, wasmlib.Key32.fromString(sc.StateRandom)); } diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts index 8dcb18cc02..c92f97dba5 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/testwasmlib.ts @@ -132,3 +132,15 @@ export function viewGetRandom(ctx: wasmlib.ScViewContext, f: sc.GetRandomContext export function funcTriggerEvent(ctx: wasmlib.ScFuncContext, f: sc.TriggerEventContext): void { f.events.test(f.params.address().value(), f.params.name().value()); } + +export function funcMapClear(ctx: wasmlib.ScFuncContext, f: sc.MapClearContext): void { +} + +export function funcMapCreate(ctx: wasmlib.ScFuncContext, f: sc.MapCreateContext): void { +} + +export function funcMapSet(ctx: wasmlib.ScFuncContext, f: sc.MapSetContext): void { +} + +export function viewMapValue(ctx: wasmlib.ScViewContext, f: sc.MapValueContext): void { +} diff --git a/contracts/wasm/testwasmlib/ts/testwasmlib/typedefs.ts b/contracts/wasm/testwasmlib/ts/testwasmlib/typedefs.ts index aab311ce94..a38bec20bd 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlib/typedefs.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlib/typedefs.ts @@ -49,3 +49,37 @@ export class ArrayOfMutableString { export class MutableStringArray extends ArrayOfMutableString { }; + +export class MapStringToImmutableString { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + getString(key: string): wasmlib.ScImmutableString { + return new wasmlib.ScImmutableString(this.objID, wasmlib.Key32.fromString(key)); + } +} + +export class ImmutableStringMap extends MapStringToImmutableString { +}; + +export class MapStringToMutableString { + objID: i32; + + constructor(objID: i32) { + this.objID = objID; + } + + clear(): void { + wasmlib.clear(this.objID); + } + + getString(key: string): wasmlib.ScMutableString { + return new wasmlib.ScMutableString(this.objID, wasmlib.Key32.fromString(key)); + } +} + +export class MutableStringMap extends MapStringToMutableString { +}; diff --git a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts index 6a144af5c3..f738825fd4 100644 --- a/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts +++ b/contracts/wasm/testwasmlib/ts/testwasmlibclient/service.ts @@ -22,6 +22,7 @@ const ArgInt16 = "int16"; const ArgInt32 = "int32"; const ArgInt64 = "int64"; const ArgInt8 = "int8"; +const ArgKey = "key"; const ArgName = "name"; const ArgParam = "this"; const ArgRecordIndex = "recordIndex"; @@ -95,6 +96,61 @@ export class ArraySetFunc extends wasmclient.ClientFunc { } } +///////////////////////////// mapClear ///////////////////////////// + +export class MapClearFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public name(v: string): void { + this.args.set(ArgName, this.args.fromString(v)); + } + + public async post(): Promise { + this.args.mandatory(ArgName); + return await super.post(0x027f215a, this.args); + } +} + +///////////////////////////// mapCreate ///////////////////////////// + +export class MapCreateFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public name(v: string): void { + this.args.set(ArgName, this.args.fromString(v)); + } + + public async post(): Promise { + this.args.mandatory(ArgName); + return await super.post(0x6295d599, this.args); + } +} + +///////////////////////////// mapSet ///////////////////////////// + +export class MapSetFunc extends wasmclient.ClientFunc { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public key(v: string): void { + this.args.set(ArgKey, this.args.fromString(v)); + } + + public name(v: string): void { + this.args.set(ArgName, this.args.fromString(v)); + } + + public value(v: string): void { + this.args.set(ArgValue, this.args.fromString(v)); + } + + public async post(): Promise { + this.args.mandatory(ArgKey); + this.args.mandatory(ArgName); + this.args.mandatory(ArgValue); + return await super.post(0xf2260404, this.args); + } +} + ///////////////////////////// paramTypes ///////////////////////////// export class ParamTypesFunc extends wasmclient.ClientFunc { @@ -352,6 +408,35 @@ export class IotaBalanceResults extends wasmclient.Results { } } +///////////////////////////// mapValue ///////////////////////////// + +export class MapValueView extends wasmclient.ClientView { + private args: wasmclient.Arguments = new wasmclient.Arguments(); + + public key(v: string): void { + this.args.set(ArgKey, this.args.fromString(v)); + } + + public name(v: string): void { + this.args.set(ArgName, this.args.fromString(v)); + } + + public async call(): Promise { + this.args.mandatory(ArgKey); + this.args.mandatory(ArgName); + const res = new MapValueResults(); + await this.callView("mapValue", this.args, res); + return res; + } +} + +export class MapValueResults extends wasmclient.Results { + + value(): string { + return this.toString(this.get(ResValue)); + } +} + ///////////////////////////// TestWasmLibService ///////////////////////////// export class TestWasmLibService extends wasmclient.Service { @@ -372,6 +457,18 @@ export class TestWasmLibService extends wasmclient.Service { return new ArraySetFunc(this); } + public mapClear(): MapClearFunc { + return new MapClearFunc(this); + } + + public mapCreate(): MapCreateFunc { + return new MapCreateFunc(this); + } + + public mapSet(): MapSetFunc { + return new MapSetFunc(this); + } + public paramTypes(): ParamTypesFunc { return new ParamTypesFunc(this); } @@ -407,4 +504,8 @@ export class TestWasmLibService extends wasmclient.Service { public iotaBalance(): IotaBalanceView { return new IotaBalanceView(this); } + + public mapValue(): MapValueView { + return new MapValueView(this); + } } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts index c12b2bb7ed..a3a39405b0 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreaccounts/service.ts @@ -61,7 +61,7 @@ export class WithdrawFunc extends wasmclient.ClientFunc { export class AccountsView extends wasmclient.ClientView { public async call(): Promise { - const res = new AccountsResults(); + const res = new AccountsResults(); await this.callView("accounts", null, res); return res; } @@ -89,7 +89,7 @@ export class BalanceView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgAgentID); - const res = new BalanceResults(); + const res = new BalanceResults(); await this.callView("balance", this.args, res); return res; } @@ -117,7 +117,7 @@ export class GetAccountNonceView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgAgentID); - const res = new GetAccountNonceResults(); + const res = new GetAccountNonceResults(); await this.callView("getAccountNonce", this.args, res); return res; } @@ -135,7 +135,7 @@ export class GetAccountNonceResults extends wasmclient.Results { export class TotalAssetsView extends wasmclient.ClientView { public async call(): Promise { - const res = new TotalAssetsResults(); + const res = new TotalAssetsResults(); await this.callView("totalAssets", null, res); return res; } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts index c79e3d5717..265fe35f9e 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblob/service.ts @@ -52,7 +52,7 @@ export class GetBlobFieldView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgField); this.args.mandatory(ArgHash); - const res = new GetBlobFieldResults(); + const res = new GetBlobFieldResults(); await this.callView("getBlobField", this.args, res); return res; } @@ -76,7 +76,7 @@ export class GetBlobInfoView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgHash); - const res = new GetBlobInfoResults(); + const res = new GetBlobInfoResults(); await this.callView("getBlobInfo", this.args, res); return res; } @@ -98,7 +98,7 @@ export class GetBlobInfoResults extends wasmclient.Results { export class ListBlobsView extends wasmclient.ClientView { public async call(): Promise { - const res = new ListBlobsResults(); + const res = new ListBlobsResults(); await this.callView("listBlobs", null, res); return res; } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts index 4e391244b0..abe2db5dc9 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreblocklog/service.ts @@ -28,7 +28,7 @@ const ResStateControllerAddress = "s"; export class ControlAddressesView extends wasmclient.ClientView { public async call(): Promise { - const res = new ControlAddressesResults(); + const res = new ControlAddressesResults(); await this.callView("controlAddresses", null, res); return res; } @@ -60,7 +60,7 @@ export class GetBlockInfoView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgBlockIndex); - const res = new GetBlockInfoResults(); + const res = new GetBlockInfoResults(); await this.callView("getBlockInfo", this.args, res); return res; } @@ -84,7 +84,7 @@ export class GetEventsForBlockView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgBlockIndex); - const res = new GetEventsForBlockResults(); + const res = new GetEventsForBlockResults(); await this.callView("getEventsForBlock", this.args, res); return res; } @@ -116,7 +116,7 @@ export class GetEventsForContractView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgContractHname); - const res = new GetEventsForContractResults(); + const res = new GetEventsForContractResults(); await this.callView("getEventsForContract", this.args, res); return res; } @@ -140,7 +140,7 @@ export class GetEventsForRequestView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgRequestID); - const res = new GetEventsForRequestResults(); + const res = new GetEventsForRequestResults(); await this.callView("getEventsForRequest", this.args, res); return res; } @@ -158,7 +158,7 @@ export class GetEventsForRequestResults extends wasmclient.Results { export class GetLatestBlockInfoView extends wasmclient.ClientView { public async call(): Promise { - const res = new GetLatestBlockInfoResults(); + const res = new GetLatestBlockInfoResults(); await this.callView("getLatestBlockInfo", null, res); return res; } @@ -186,7 +186,7 @@ export class GetRequestIDsForBlockView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgBlockIndex); - const res = new GetRequestIDsForBlockResults(); + const res = new GetRequestIDsForBlockResults(); await this.callView("getRequestIDsForBlock", this.args, res); return res; } @@ -210,7 +210,7 @@ export class GetRequestReceiptView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgRequestID); - const res = new GetRequestReceiptResults(); + const res = new GetRequestReceiptResults(); await this.callView("getRequestReceipt", this.args, res); return res; } @@ -242,7 +242,7 @@ export class GetRequestReceiptsForBlockView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgBlockIndex); - const res = new GetRequestReceiptsForBlockResults(); + const res = new GetRequestReceiptsForBlockResults(); await this.callView("getRequestReceiptsForBlock", this.args, res); return res; } @@ -266,7 +266,7 @@ export class IsRequestProcessedView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgRequestID); - const res = new IsRequestProcessedResults(); + const res = new IsRequestProcessedResults(); await this.callView("isRequestProcessed", this.args, res); return res; } diff --git a/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts b/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts index 1ff7e42263..f30da2d644 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coregovernance/service.ts @@ -184,7 +184,7 @@ export class SetDefaultFeeFunc extends wasmclient.ClientFunc { export class GetAllowedStateControllerAddressesView extends wasmclient.ClientView { public async call(): Promise { - const res = new GetAllowedStateControllerAddressesResults(); + const res = new GetAllowedStateControllerAddressesResults(); await this.callView("getAllowedStateControllerAddresses", null, res); return res; } @@ -202,7 +202,7 @@ export class GetAllowedStateControllerAddressesResults extends wasmclient.Result export class GetChainInfoView extends wasmclient.ClientView { public async call(): Promise { - const res = new GetChainInfoResults(); + const res = new GetChainInfoResults(); await this.callView("getChainInfo", null, res); return res; } @@ -258,7 +258,7 @@ export class GetFeeInfoView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgHname); - const res = new GetFeeInfoResults(); + const res = new GetFeeInfoResults(); await this.callView("getFeeInfo", this.args, res); return res; } @@ -284,7 +284,7 @@ export class GetFeeInfoResults extends wasmclient.Results { export class GetMaxBlobSizeView extends wasmclient.ClientView { public async call(): Promise { - const res = new GetMaxBlobSizeResults(); + const res = new GetMaxBlobSizeResults(); await this.callView("getMaxBlobSize", null, res); return res; } diff --git a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts index f661a112c9..b00ab43f67 100644 --- a/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts +++ b/packages/vm/wasmlib/ts/wasmclient/coreroot/service.ts @@ -82,7 +82,7 @@ export class FindContractView extends wasmclient.ClientView { public async call(): Promise { this.args.mandatory(ArgHname); - const res = new FindContractResults(); + const res = new FindContractResults(); await this.callView("findContract", this.args, res); return res; } @@ -104,7 +104,7 @@ export class FindContractResults extends wasmclient.Results { export class GetContractRecordsView extends wasmclient.ClientView { public async call(): Promise { - const res = new GetContractRecordsResults(); + const res = new GetContractRecordsResults(); await this.callView("getContractRecords", null, res); return res; } diff --git a/tools/schema/generator/tsclienttemplates/service.go b/tools/schema/generator/tsclienttemplates/service.go index 9ec9ca2b22..e27327d93d 100644 --- a/tools/schema/generator/tsclienttemplates/service.go +++ b/tools/schema/generator/tsclienttemplates/service.go @@ -87,7 +87,7 @@ $#if param execWithArgs execNoArgs public async call(): Promise<$FuncName$+Results> { $#each mandatory mandatoryCheck $#if param execWithArgs execNoArgs - const res = new $FuncName$+Results(); + const res = new $FuncName$+Results(); await this.callView("$funcName", $args, res); return res; } diff --git a/tools/schema/generator/tstemplates/contract.go b/tools/schema/generator/tstemplates/contract.go index a2cc9ba26b..51788b0edb 100644 --- a/tools/schema/generator/tstemplates/contract.go +++ b/tools/schema/generator/tstemplates/contract.go @@ -39,7 +39,7 @@ $#if events PackageEventsExist `, // ******************************* "PackageEventsExist": ` - events: sc.$Package$+Events = new sc.$Package$+Events(); + events: sc.$Package$+Events = new sc.$Package$+Events(); `, // ******************************* "ImmutableFuncNameParams": ` From 96e45a170b0c0a9127f8bc8d41949766daa0a91b Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Tue, 11 Jan 2022 15:01:11 +0200 Subject: [PATCH 096/120] Put PubKey to the Chain PeerStatus. --- packages/chain/chain.go | 3 ++- packages/chain/committee/committee.go | 3 ++- packages/dashboard/mock_test.go | 14 ++++++++++++-- packages/dashboard/templates/chain.tmpl | 12 +++++++----- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/chain/chain.go b/packages/chain/chain.go index c4d72a7d8c..9ae6d11cc1 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -226,7 +226,8 @@ type CommitteeInfo struct { type PeerStatus struct { Index int - PeeringID string + PubKey *ed25519.PublicKey + NetID string Connected bool } diff --git a/packages/chain/committee/committee.go b/packages/chain/committee/committee.go index 8932865e0d..127c38d4f4 100644 --- a/packages/chain/committee/committee.go +++ b/packages/chain/committee/committee.go @@ -149,7 +149,8 @@ func (c *committee) PeerStatus() []*chain.PeerStatus { for i, peer := range c.validatorNodes.AllNodes() { status := &chain.PeerStatus{ Index: int(i), - PeeringID: peer.NetID(), + NetID: peer.NetID(), + PubKey: peer.PubKey(), Connected: peer.IsAlive(), } ret = append(ret, status) diff --git a/packages/dashboard/mock_test.go b/packages/dashboard/mock_test.go index d1b6e307fc..a21a72925c 100644 --- a/packages/dashboard/mock_test.go +++ b/packages/dashboard/mock_test.go @@ -104,6 +104,14 @@ func (w *waspServicesMock) GetChainCommitteeInfo(chainID *iscp.ChainID) (*chain. if !ok { return nil, xerrors.Errorf("chain not found") } + pubKey0, err := ed25519.PublicKeyFromString("AaKwV3ezdM8DcGKwJ6eRaJ2946D1yghqfpBDatGip1dX") + if err != nil { + return nil, err + } + pubKey1, err := ed25519.PublicKeyFromString("AaKwV3ezdM8DcGKwJ6eRaJ2946D1yghqfpBDatGip1dX") + if err != nil { + return nil, err + } return &chain.CommitteeInfo{ Address: ledgerstate.NewED25519Address(ed25519.PublicKey{}), Size: 2, @@ -112,12 +120,14 @@ func (w *waspServicesMock) GetChainCommitteeInfo(chainID *iscp.ChainID) (*chain. PeerStatus: []*chain.PeerStatus{ { Index: 0, - PeeringID: "0", + NetID: "localhost:2000", + PubKey: &pubKey0, Connected: true, }, { Index: 1, - PeeringID: "1", + NetID: "localhost:2001", + PubKey: &pubKey1, Connected: true, }, }, diff --git a/packages/dashboard/templates/chain.tmpl b/packages/dashboard/templates/chain.tmpl index aeec2d65ed..5638d6af0f 100644 --- a/packages/dashboard/templates/chain.tmpl +++ b/packages/dashboard/templates/chain.tmpl @@ -88,16 +88,17 @@

    Committee

    Address
    {{template "address" .Committee.Address}}
    -
    Size
    {{.Committee.Size}}
    -
    Quorum
    {{.Committee.Quorum}}
    -
    Quorum status
    {{if .Committee.QuorumIsAlive}}up{{else}}down{{end}}
    +
    Size
    {{.Committee.Size}}
    +
    Quorum
    {{.Committee.Quorum}}
    +
    Quorum status
    {{if .Committee.QuorumIsAlive}}up{{else}}down{{end}}

    Peer status

    - + + @@ -105,7 +106,8 @@ {{range $_, $s := .Committee.PeerStatus}} - + + {{end}} From 9b4e848b2c86b0922a614d758c258f1fa1a15fdd Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Tue, 11 Jan 2022 15:50:26 +0200 Subject: [PATCH 097/120] Consensus status exported to webapi --- packages/chain/chain.go | 23 ++++++ packages/chain/chainimpl/metrics.go | 8 ++ packages/chain/consensus/consensus.go | 6 +- packages/chain/consensus/workflow_status.go | 60 ++++++++------- ...hainnodeconnmetrics.go => chainmetrics.go} | 76 ++++++++++++++++--- packages/webapi/admapi/endpoints.go | 2 +- packages/webapi/model/consensus_metrics.go | 51 +++++++++++++ packages/webapi/request/request_test.go | 4 + packages/webapi/routes/routes.go | 4 + 9 files changed, 194 insertions(+), 40 deletions(-) rename packages/webapi/admapi/{chainnodeconnmetrics.go => chainmetrics.go} (52%) create mode 100644 packages/webapi/model/consensus_metrics.go diff --git a/packages/chain/chain.go b/packages/chain/chain.go index ef7b0a1f9f..99454ecec0 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -59,6 +59,7 @@ type ChainRequests interface { type ChainMetrics interface { GetNodeConnectionMetrics() nodeconnmetrics.NodeConnectionMessagesMetrics + GetConsensusWorkflowStatus() ConsensusWorkflowStatus } type Chain interface { @@ -158,6 +159,7 @@ type Consensus interface { IsReady() bool Close() GetStatusSnapshot() *ConsensusInfo + GetWorkflowStatus() ConsensusWorkflowStatus ShouldReceiveMissingRequest(req iscp.Request) bool } @@ -206,6 +208,27 @@ type ConsensusInfo struct { TimerTick int } +type ConsensusWorkflowStatus interface { + IsStateReceived() bool + IsBatchProposalSent() bool + IsConsensusBatchKnown() bool + IsVMStarted() bool + IsVMResultSigned() bool + IsTransactionFinalized() bool + IsTransactionPosted() bool + IsTransactionSeen() bool + IsInProgress() bool + + GetBatchProposalSentTime() time.Time + GetConsensusBatchKnownTime() time.Time + GetVMStartedTime() time.Time + GetVMResultSignedTime() time.Time + GetTransactionFinalizedTime() time.Time + GetTransactionPostedTime() time.Time + GetTransactionSeenTime() time.Time + GetCompletedTime() time.Time +} + type ReadyListRecord struct { Request iscp.Request Seen map[uint16]bool diff --git a/packages/chain/chainimpl/metrics.go b/packages/chain/chainimpl/metrics.go index 650f96769d..2b5f338213 100644 --- a/packages/chain/chainimpl/metrics.go +++ b/packages/chain/chainimpl/metrics.go @@ -5,9 +5,17 @@ package chainimpl import ( + "github.com/iotaledger/wasp/packages/chain" "github.com/iotaledger/wasp/packages/metrics/nodeconnmetrics" ) func (c *chainObj) GetNodeConnectionMetrics() nodeconnmetrics.NodeConnectionMessagesMetrics { return c.nodeConn.GetMetrics() } + +func (c *chainObj) GetConsensusWorkflowStatus() chain.ConsensusWorkflowStatus { + if c.consensus == nil { + return nil + } + return c.consensus.GetWorkflowStatus() +} diff --git a/packages/chain/consensus/consensus.go b/packages/chain/consensus/consensus.go index 169057d5af..789252a96c 100644 --- a/packages/chain/consensus/consensus.go +++ b/packages/chain/consensus/consensus.go @@ -40,7 +40,7 @@ type consensus struct { iAmContributor bool myContributionSeqNumber uint16 contributors []uint16 - workflow *WorkflowStatus + workflow *workflowStatus delayBatchProposalUntil time.Time delayRunVMUntil time.Time delaySendingSignedResult time.Time @@ -283,3 +283,7 @@ func (c *consensus) GetStatusSnapshot() *chain.ConsensusInfo { } return ret.(*chain.ConsensusInfo) } + +func (c *consensus) GetWorkflowStatus() chain.ConsensusWorkflowStatus { + return c.workflow +} diff --git a/packages/chain/consensus/workflow_status.go b/packages/chain/consensus/workflow_status.go index e19f437bb0..65726d0178 100644 --- a/packages/chain/consensus/workflow_status.go +++ b/packages/chain/consensus/workflow_status.go @@ -5,9 +5,11 @@ package consensus import ( "time" + + "github.com/iotaledger/wasp/packages/chain" ) -type WorkflowStatus struct { +type workflowStatus struct { flagStateReceived bool flagBatchProposalSent bool flagConsensusBatchKnown bool @@ -28,117 +30,119 @@ type WorkflowStatus struct { timeCompleted time.Time } -func newWorkflowStatus(stateReceived bool) *WorkflowStatus { - return &WorkflowStatus{ +var _ chain.ConsensusWorkflowStatus = &workflowStatus{} + +func newWorkflowStatus(stateReceived bool) *workflowStatus { + return &workflowStatus{ flagStateReceived: stateReceived, flagInProgress: stateReceived, } } -func (wsT *WorkflowStatus) setBatchProposalSent() { +func (wsT *workflowStatus) setBatchProposalSent() { wsT.flagBatchProposalSent = true wsT.timeBatchProposalSent = time.Now() } -func (wsT *WorkflowStatus) setConsensusBatchKnown() { +func (wsT *workflowStatus) setConsensusBatchKnown() { wsT.flagConsensusBatchKnown = true wsT.timeConsensusBatchKnown = time.Now() } -func (wsT *WorkflowStatus) setVMStarted() { +func (wsT *workflowStatus) setVMStarted() { wsT.flagVMStarted = true wsT.timeVMStarted = time.Now() } -func (wsT *WorkflowStatus) setVMResultSigned() { +func (wsT *workflowStatus) setVMResultSigned() { wsT.flagVMResultSigned = true wsT.timeVMResultSigned = time.Now() } -func (wsT *WorkflowStatus) setTransactionFinalized() { +func (wsT *workflowStatus) setTransactionFinalized() { wsT.flagTransactionFinalized = true wsT.timeTransactionFinalized = time.Now() } -func (wsT *WorkflowStatus) setTransactionPosted() { +func (wsT *workflowStatus) setTransactionPosted() { wsT.flagTransactionPosted = true wsT.timeTransactionPosted = time.Now() } -func (wsT *WorkflowStatus) setTransactionSeen() { +func (wsT *workflowStatus) setTransactionSeen() { wsT.flagTransactionSeen = true wsT.timeTransactionSeen = time.Now() } -func (wsT *WorkflowStatus) setCompleted() { +func (wsT *workflowStatus) setCompleted() { wsT.flagInProgress = false wsT.timeCompleted = time.Now() } -func (wsT *WorkflowStatus) IsStateReceived() bool { +func (wsT *workflowStatus) IsStateReceived() bool { return wsT.flagStateReceived } -func (wsT *WorkflowStatus) IsBatchProposalSent() bool { +func (wsT *workflowStatus) IsBatchProposalSent() bool { return wsT.flagBatchProposalSent } -func (wsT *WorkflowStatus) IsConsensusBatchKnown() bool { +func (wsT *workflowStatus) IsConsensusBatchKnown() bool { return wsT.flagConsensusBatchKnown } -func (wsT *WorkflowStatus) IsVMStarted() bool { +func (wsT *workflowStatus) IsVMStarted() bool { return wsT.flagVMStarted } -func (wsT *WorkflowStatus) IsVMResultSigned() bool { +func (wsT *workflowStatus) IsVMResultSigned() bool { return wsT.flagVMResultSigned } -func (wsT *WorkflowStatus) IsTransactionFinalized() bool { +func (wsT *workflowStatus) IsTransactionFinalized() bool { return wsT.flagTransactionFinalized } -func (wsT *WorkflowStatus) IsTransactionPosted() bool { +func (wsT *workflowStatus) IsTransactionPosted() bool { return wsT.flagTransactionPosted } -func (wsT *WorkflowStatus) IsTransactionSeen() bool { +func (wsT *workflowStatus) IsTransactionSeen() bool { return wsT.flagTransactionSeen } -func (wsT *WorkflowStatus) IsInProgress() bool { +func (wsT *workflowStatus) IsInProgress() bool { return wsT.flagInProgress } -func (wsT *WorkflowStatus) GetBatchProposalSentTime() time.Time { +func (wsT *workflowStatus) GetBatchProposalSentTime() time.Time { return wsT.timeBatchProposalSent } -func (wsT *WorkflowStatus) GetConsensusBatchKnownTime() time.Time { +func (wsT *workflowStatus) GetConsensusBatchKnownTime() time.Time { return wsT.timeConsensusBatchKnown } -func (wsT *WorkflowStatus) GetVMStartedTime() time.Time { +func (wsT *workflowStatus) GetVMStartedTime() time.Time { return wsT.timeVMStarted } -func (wsT *WorkflowStatus) GetVMResultSignedTime() time.Time { +func (wsT *workflowStatus) GetVMResultSignedTime() time.Time { return wsT.timeVMResultSigned } -func (wsT *WorkflowStatus) GetTransactionFinalizedTime() time.Time { +func (wsT *workflowStatus) GetTransactionFinalizedTime() time.Time { return wsT.timeTransactionFinalized } -func (wsT *WorkflowStatus) GetTransactionPostedTime() time.Time { +func (wsT *workflowStatus) GetTransactionPostedTime() time.Time { return wsT.timeTransactionPosted } -func (wsT *WorkflowStatus) GetTransactionSeenTime() time.Time { +func (wsT *workflowStatus) GetTransactionSeenTime() time.Time { return wsT.timeTransactionSeen } -func (wsT *WorkflowStatus) GetCompletedTime() time.Time { +func (wsT *workflowStatus) GetCompletedTime() time.Time { return wsT.timeCompleted } diff --git a/packages/webapi/admapi/chainnodeconnmetrics.go b/packages/webapi/admapi/chainmetrics.go similarity index 52% rename from packages/webapi/admapi/chainnodeconnmetrics.go rename to packages/webapi/admapi/chainmetrics.go index cc7f442458..ecf32d8137 100644 --- a/packages/webapi/admapi/chainnodeconnmetrics.go +++ b/packages/webapi/admapi/chainmetrics.go @@ -5,6 +5,7 @@ import ( "net/http" "time" + "github.com/iotaledger/wasp/packages/chain" "github.com/iotaledger/wasp/packages/chains" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/webapi/httperrors" @@ -14,7 +15,13 @@ import ( "github.com/pangpanglabs/echoswagger/v2" ) -func addChainStatsEndpoints(adm echoswagger.ApiGroup, chainsProvider chains.Provider) { +func addChainMetricsEndpoints(adm echoswagger.ApiGroup, chainsProvider chains.Provider) { + cms := &chainMetricsService{chainsProvider} + addChainNodeConnMetricsEndpoints(adm, cms) + addChainConsensusMetricsEndpoints(adm, cms) +} + +func addChainNodeConnMetricsEndpoints(adm echoswagger.ApiGroup, cms *chainMetricsService) { chainExample := &model.NodeConnectionMessagesMetrics{ OutPullState: &model.NodeConnectionMessageMetrics{ Total: 15, @@ -66,30 +73,56 @@ func addChainStatsEndpoints(adm echoswagger.ApiGroup, chainsProvider chains.Prov }, } - s := &chainStatsService{chainsProvider} + adm.GET(routes.GetChainsNodeConnectionMetrics(), cms.handleGetChainsNodeConnMetrics). + SetSummary("Get cummulative chains node connection metrics"). + AddResponse(http.StatusOK, "Chains Metrics", example, nil) + + adm.GET(routes.GetChainNodeConnectionMetrics(":chainID"), cms.handleGetChainNodeConnMetrics). + SetSummary("Get chain node connection metrics for the given chain ID"). + AddParamPath("", "chainID", "ChainID (base58)"). + AddResponse(http.StatusOK, "Chain Metrics", chainExample, nil) +} + +func addChainConsensusMetricsEndpoints(adm echoswagger.ApiGroup, cms *chainMetricsService) { + example := &model.ConsensusWorkflowStatus{ + FlagStateReceived: true, + FlagBatchProposalSent: true, + FlagConsensusBatchKnown: true, + FlagVMStarted: false, + FlagVMResultSigned: false, + FlagTransactionFinalized: false, + FlagTransactionPosted: false, + FlagTransactionSeen: false, + FlagInProgress: true, - adm.GET(routes.GetChainsNodeConnectionMetrics(), s.handleGetChainsStats). - SetSummary("Get cummulative chains state statistics"). - AddResponse(http.StatusOK, "Chains Stats", example, nil) + TimeBatchProposalSent: time.Now().Add(-10 * time.Second), + TimeConsensusBatchKnown: time.Now().Add(-5 * time.Second), + TimeVMStarted: time.Time{}, + TimeVMResultSigned: time.Time{}, + TimeTransactionFinalized: time.Time{}, + TimeTransactionPosted: time.Time{}, + TimeTransactionSeen: time.Time{}, + TimeCompleted: time.Time{}, + } - adm.GET(routes.GetChainNodeConnectionMetrics(":chainID"), s.handleGetChainStats). + adm.GET(routes.GetChainConsensusWorkflowStatus(":chainID"), cms.handleGetChainConsensusWorkflowStatus). SetSummary("Get chain state statistics for the given chain ID"). AddParamPath("", "chainID", "ChainID (base58)"). - AddResponse(http.StatusOK, "Chain Stats", chainExample, nil) + AddResponse(http.StatusOK, "Chain Stats", example, nil) } -type chainStatsService struct { +type chainMetricsService struct { chains chains.Provider } -func (cssT *chainStatsService) handleGetChainsStats(c echo.Context) error { +func (cssT *chainMetricsService) handleGetChainsNodeConnMetrics(c echo.Context) error { metrics := cssT.chains().GetNodeConnectionMetrics() metricsModel := model.NewNodeConnectionMetrics(metrics) return c.JSON(http.StatusOK, metricsModel) } -func (cssT *chainStatsService) handleGetChainStats(c echo.Context) error { +func (cssT *chainMetricsService) handleGetChainNodeConnMetrics(c echo.Context) error { chainID, err := iscp.ChainIDFromBase58(c.Param("chainID")) if err != nil { return httperrors.BadRequest(err.Error()) @@ -103,3 +136,26 @@ func (cssT *chainStatsService) handleGetChainStats(c echo.Context) error { return c.JSON(http.StatusOK, metricsModel) } + +func (cssT *chainMetricsService) handleGetChainConsensusWorkflowStatus(c echo.Context) error { + theChain, err := cssT.getChain(c) + if err != nil { + return err + } + status := theChain.GetConsensusWorkflowStatus() + statusModel := model.NewConsensusWorkflowStatus(status) + + return c.JSON(http.StatusOK, statusModel) +} + +func (cssT *chainMetricsService) getChain(c echo.Context) (chain.Chain, error) { + chainID, err := iscp.ChainIDFromBase58(c.Param("chainID")) + if err != nil { + return nil, httperrors.BadRequest(err.Error()) + } + theChain := cssT.chains().Get(chainID) + if theChain == nil { + return nil, httperrors.NotFound(fmt.Sprintf("Active chain %s not found", chainID)) + } + return theChain, nil +} diff --git a/packages/webapi/admapi/endpoints.go b/packages/webapi/admapi/endpoints.go index 83df3e783b..e1608243fd 100644 --- a/packages/webapi/admapi/endpoints.go +++ b/packages/webapi/admapi/endpoints.go @@ -47,7 +47,7 @@ func AddEndpoints( addShutdownEndpoint(adm, shutdown) addChainRecordEndpoints(adm, registryProvider) - addChainStatsEndpoints(adm, chainsProvider) + addChainMetricsEndpoints(adm, chainsProvider) addCommitteeRecordEndpoints(adm, registryProvider, chainsProvider) addChainEndpoints(adm, registryProvider, chainsProvider, metrics) addDKSharesEndpoints(adm, registryProvider, nodeProvider) diff --git a/packages/webapi/model/consensus_metrics.go b/packages/webapi/model/consensus_metrics.go new file mode 100644 index 0000000000..be1841eed4 --- /dev/null +++ b/packages/webapi/model/consensus_metrics.go @@ -0,0 +1,51 @@ +package model + +import ( + "time" + + "github.com/iotaledger/wasp/packages/chain" +) + +type ConsensusWorkflowStatus struct { + FlagStateReceived bool `swagger:"desc(Shows if state output is received in current consensus iteration)"` + FlagBatchProposalSent bool `swagger:"desc(Shows if batch proposal is sent out in current consensus iteration)"` + FlagConsensusBatchKnown bool `swagger:"desc(Shows if consensus on batch is reached and known in current consensus iteration)"` + FlagVMStarted bool `swagger:"desc(Shows if virtual machine is started in current consensus iteration)"` + FlagVMResultSigned bool `swagger:"desc(Shows if virtual machine has returned its results in current consensus iteration)"` + FlagTransactionFinalized bool `swagger:"desc(Shows if consensus on transaction is reached in current consensus iteration)"` + FlagTransactionPosted bool `swagger:"desc(Shows if transaction is posted to L1 in current consensus iteration)"` + FlagTransactionSeen bool `swagger:"desc(Shows if L1 reported that it has seen the transaction of current consensus iteration)"` + FlagInProgress bool `swagger:"desc(Shows if consensus algorithm is still not completed in current consensus iteration)"` + + TimeBatchProposalSent time.Time `swagger:"desc(Shows when batch proposal was last sent out in current consensus iteration)"` + TimeConsensusBatchKnown time.Time `swagger:"desc(Shows when ACS results of consensus on batch was last received in current consensus iteration)"` + TimeVMStarted time.Time `swagger:"desc(Shows when virtual machine was last started in current consensus iteration)"` + TimeVMResultSigned time.Time `swagger:"desc(Shows when virtual machine results were last received and signed in current consensus iteration)"` + TimeTransactionFinalized time.Time `swagger:"desc(Shows when algorithm last noted that all the data for consensus on transaction had been received in current consensus iteration)"` + TimeTransactionPosted time.Time `swagger:"desc(Shows when transaction was last posted to L1 in current consensus iteration)"` + TimeTransactionSeen time.Time `swagger:"desc(Shows when algorithm last noted that transaction hadd been seen by L1 in current consensus iteration)"` + TimeCompleted time.Time `swagger:"desc(Shows when algorithm was last completed in current consensus iteration)"` +} + +func NewConsensusWorkflowStatus(status chain.ConsensusWorkflowStatus) *ConsensusWorkflowStatus { + return &ConsensusWorkflowStatus{ + FlagStateReceived: status.IsStateReceived(), + FlagBatchProposalSent: status.IsBatchProposalSent(), + FlagConsensusBatchKnown: status.IsConsensusBatchKnown(), + FlagVMStarted: status.IsVMStarted(), + FlagVMResultSigned: status.IsVMResultSigned(), + FlagTransactionFinalized: status.IsTransactionFinalized(), + FlagTransactionPosted: status.IsTransactionPosted(), + FlagTransactionSeen: status.IsTransactionSeen(), + FlagInProgress: status.IsInProgress(), + + TimeBatchProposalSent: status.GetBatchProposalSentTime(), + TimeConsensusBatchKnown: status.GetConsensusBatchKnownTime(), + TimeVMStarted: status.GetVMStartedTime(), + TimeVMResultSigned: status.GetVMResultSignedTime(), + TimeTransactionFinalized: status.GetTransactionFinalizedTime(), + TimeTransactionPosted: status.GetTransactionPostedTime(), + TimeTransactionSeen: status.GetTransactionSeenTime(), + TimeCompleted: status.GetCompletedTime(), + } +} diff --git a/packages/webapi/request/request_test.go b/packages/webapi/request/request_test.go index 028c8295e6..ecf7553448 100644 --- a/packages/webapi/request/request_test.go +++ b/packages/webapi/request/request_test.go @@ -72,6 +72,10 @@ func (m *mockedChain) GetNodeConnectionMetrics() nodeconnmetrics.NodeConnectionM panic("implement me") } +func (m *mockedChain) GetConsensusWorkflowStatus() chain.ConsensusWorkflowStatus { + panic("implement me") +} + // private methods func createMockedGetChain(t *testing.T) chains.ChainProvider { diff --git a/packages/webapi/routes/routes.go b/packages/webapi/routes/routes.go index 1cbcc0e815..5fe51892d1 100644 --- a/packages/webapi/routes/routes.go +++ b/packages/webapi/routes/routes.go @@ -63,6 +63,10 @@ func GetChainNodeConnectionMetrics(chainID string) string { return "/adm/chain/" + chainID + "/nodeconn/metrics" } +func GetChainConsensusWorkflowStatus(chainID string) string { + return "/adm/chain/" + chainID + "/consensus/status" +} + func GetCommitteeForChain(chainID string) string { return "/adm/chain/" + chainID + "/committeerecord" } From e8e1fd0049eb5b1ba775f806d0a9122ccd27b4f0 Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Tue, 11 Jan 2022 16:29:06 +0200 Subject: [PATCH 098/120] Consensus workflow status exported to cli --- client/metrics.go | 9 ++++++ tools/wasp-cli/metrics/consensus.go | 50 +++++++++++++++++++++++++++++ tools/wasp-cli/metrics/metrics.go | 1 + 3 files changed, 60 insertions(+) create mode 100644 tools/wasp-cli/metrics/consensus.go diff --git a/client/metrics.go b/client/metrics.go index ec241a3a89..6e4804b537 100644 --- a/client/metrics.go +++ b/client/metrics.go @@ -25,3 +25,12 @@ func (c *WaspClient) GetChainNodeConnectionMetrics(chID *iscp.ChainID) (*model.N } return ncmm, nil } + +// GetNodeConnectionMetrics fetches a consensus workflow status by address +func (c *WaspClient) GetChainConsensusWorkflowStatus(chID *iscp.ChainID) (*model.ConsensusWorkflowStatus, error) { + ncmm := &model.ConsensusWorkflowStatus{} + if err := c.do(http.MethodGet, routes.GetChainConsensusWorkflowStatus(chID.Base58()), nil, ncmm); err != nil { + return nil, err + } + return ncmm, nil +} diff --git a/tools/wasp-cli/metrics/consensus.go b/tools/wasp-cli/metrics/consensus.go new file mode 100644 index 0000000000..506e6a767f --- /dev/null +++ b/tools/wasp-cli/metrics/consensus.go @@ -0,0 +1,50 @@ +package metrics + +import ( + "fmt" + "time" + + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/tools/wasp-cli/config" + "github.com/iotaledger/wasp/tools/wasp-cli/log" + "github.com/spf13/cobra" +) + +var timestampNeverConst = time.Time{} + +var consensusMetricsCmd = &cobra.Command{ + Use: "consensus", + Short: "Show current value of collected metrics of consensus", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + client := config.WaspClient() + chid, err := iscp.ChainIDFromBase58(chainIDStr) + log.Check(err) + workflowStatus, err := client.GetChainConsensusWorkflowStatus(chid) + log.Check(err) + header := []string{"Flag name", "Value", "Last time set"} + table := make([][]string, 9) + table[0] = makeWorkflowTableRow("State received", workflowStatus.FlagStateReceived, time.Time{}) + table[1] = makeWorkflowTableRow("Batch proposal sent", workflowStatus.FlagBatchProposalSent, workflowStatus.TimeBatchProposalSent) + table[2] = makeWorkflowTableRow("Consensus on batch reached", workflowStatus.FlagConsensusBatchKnown, workflowStatus.TimeConsensusBatchKnown) + table[3] = makeWorkflowTableRow("Virtual machine started", workflowStatus.FlagVMStarted, workflowStatus.TimeVMStarted) + table[4] = makeWorkflowTableRow("Virtual machine result signed", workflowStatus.FlagVMResultSigned, workflowStatus.TimeVMResultSigned) + table[5] = makeWorkflowTableRow("Transaction finalized", workflowStatus.FlagTransactionFinalized, workflowStatus.TimeTransactionFinalized) + table[6] = makeWorkflowTableRow("Transaction posted to L1", workflowStatus.FlagTransactionPosted, workflowStatus.TimeTransactionPosted) // TODO: is not meaningful, if I am not a contributor + table[7] = makeWorkflowTableRow("Transaction seen by L1", workflowStatus.FlagTransactionSeen, workflowStatus.TimeTransactionSeen) + table[8] = makeWorkflowTableRow("Consensus in progress", workflowStatus.FlagInProgress, workflowStatus.TimeCompleted) + log.PrintTable(header, table) + }, +} + +func makeWorkflowTableRow(name string, value bool, timestamp time.Time) []string { + res := make([]string, 3) + res[0] = name + res[1] = fmt.Sprintf("%v", value) + if timestamp == timestampNeverConst { + res[2] = "" + } else { + res[2] = timestamp.String() + } + return res +} diff --git a/tools/wasp-cli/metrics/metrics.go b/tools/wasp-cli/metrics/metrics.go index b14acd0388..77e9256f30 100644 --- a/tools/wasp-cli/metrics/metrics.go +++ b/tools/wasp-cli/metrics/metrics.go @@ -20,5 +20,6 @@ func Init(rootCmd *cobra.Command) { rootCmd.AddCommand(metricsCmd) metricsCmd.AddCommand(nodeconnMetricsCmd) + metricsCmd.AddCommand(consensusMetricsCmd) metricsCmd.PersistentFlags().StringVarP(&chainIDStr, "chain", "", "", "chain ID for which metrics should be displayed") } From beaa0276ea4778b8dfa1fee8ce509928fb14b848 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Tue, 11 Jan 2022 16:57:53 +0200 Subject: [PATCH 099/120] An attempt to make the code more clear. --- packages/chain/chainimpl/eventproc.go | 9 +---- packages/peering/domain/domain.go | 54 +++++++++++++-------------- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/packages/chain/chainimpl/eventproc.go b/packages/chain/chainimpl/eventproc.go index 3f434ca00a..5a351f7163 100644 --- a/packages/chain/chainimpl/eventproc.go +++ b/packages/chain/chainimpl/eventproc.go @@ -190,17 +190,12 @@ func (c *chainObj) getChainDKShare(addr ledgerstate.Address) (*tcrypto.DKShare, if err != nil { return nil, err } - found := false for i := range cmtDKShare.NodePubKeys { if *cmtDKShare.NodePubKeys[i] == *selfPubKey { - found = true - break + return cmtDKShare, nil } } - if !found { - return nil, xerrors.Errorf("createCommitteeIfNeeded: I am not among nodes of the committee record. Inconsistency") - } - return cmtDKShare, nil + return nil, xerrors.Errorf("createCommitteeIfNeeded: I am not among nodes of the committee record. Inconsistency") } func (c *chainObj) createNewCommitteeAndConsensus(dkShare *tcrypto.DKShare) error { diff --git a/packages/peering/domain/domain.go b/packages/peering/domain/domain.go index 005068cdbd..c3395158a5 100644 --- a/packages/peering/domain/domain.go +++ b/packages/peering/domain/domain.go @@ -76,47 +76,47 @@ func (d *DomainImpl) GetRandomOtherPeers(upToNumPeers int) []*ed25519.PublicKey func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { d.mutex.RLock() - nodes := make(map[ed25519.PublicKey]peering.PeerSender) + oldPeers := make(map[ed25519.PublicKey]peering.PeerSender) // A copy, to avoid keeping the lock. for k, v := range d.nodes { - nodes[k] = v + oldPeers[k] = v } d.mutex.RUnlock() + nodes := make(map[ed25519.PublicKey]peering.PeerSender) // Will collect the new set of nodes. changed := false // // Add new peers. for _, newPeerPubKey := range newPeerPubKeys { - found := false - for _, existingPeer := range nodes { - if *existingPeer.PubKey() == *newPeerPubKey { - found = true - break - } + if _, isOldPeer := oldPeers[*newPeerPubKey]; isOldPeer { + continue // Old peers will be retained bellow. } - if !found { - newPeerSender, err := d.netProvider.PeerByPubKey(newPeerPubKey) - if err != nil { - // TODO: Maybe more control should be needed here. Distinguish the mandatory and the optional nodes? - d.log.Warnf("Peer with pubKey=%v not found, will be ignored for now, reason: %v", newPeerPubKey.String(), err) - } else { - changed = true - nodes[*newPeerSender.PubKey()] = newPeerSender - d.log.Infof("Domain peer added, pubKey=%v, netID=%v", newPeerSender.PubKey().String(), newPeerSender.NetID()) - } + newPeerSender, err := d.netProvider.PeerByPubKey(newPeerPubKey) + if err != nil { + d.log.Warnf("Domain peer skipped for now, pubKey=%v not found, reason: %v", newPeerPubKey.String(), err) + continue } + changed = true + nodes[*newPeerSender.PubKey()] = newPeerSender + d.log.Infof("Domain peer added, pubKey=%v, netID=%v", newPeerSender.PubKey().String(), newPeerSender.NetID()) } // - // Remove peers that are not needed anymore. - for _, oldPeer := range nodes { - found := false - for _, newPeerPubKey := range newPeerPubKeys { - if *oldPeer.PubKey() == *newPeerPubKey { - found = true - break + // Remove peers that are not needed anymore and retain others. + for _, oldPeer := range oldPeers { + oldPeerDropped := true + if *oldPeer.PubKey() == *d.netProvider.Self().PubKey() { + // We retain the current node in the domain all the time. + nodes[*oldPeer.PubKey()] = oldPeer + oldPeerDropped = false + } else { + for _, newPeerPubKey := range newPeerPubKeys { + if *oldPeer.PubKey() == *newPeerPubKey { + nodes[*oldPeer.PubKey()] = oldPeer + oldPeerDropped = false + break + } } } - if !found && (*oldPeer.PubKey() != *d.netProvider.Self().PubKey()) { + if oldPeerDropped { changed = true - delete(nodes, *oldPeer.PubKey()) d.log.Infof("Domain peer removed, pubKey=%v, netID=%v", oldPeer.PubKey().String(), oldPeer.NetID()) } } From e49a822279a75aaf5432dea9837316a1087d162a Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Tue, 11 Jan 2022 18:07:04 +0200 Subject: [PATCH 100/120] Dashboard metrics tab reorganised to prepare to include consensus --- packages/dashboard/metrics.go | 9 +++- packages/dashboard/metrics_chain.go | 44 +++++++++++++++++ packages/dashboard/metrics_chain_nodeconn.go | 49 +++++++++++++++++++ packages/dashboard/metrics_nodeconn.go | 8 +-- .../dashboard/metrics_nodeconn_messages.go | 49 ------------------- packages/dashboard/templates/metrics.tmpl | 15 +++++- .../dashboard/templates/metrics_chain.tmpl | 10 ++++ ...sages.tmpl => metrics_chain_nodeconn.tmpl} | 0 .../dashboard/templates/metrics_nodeconn.tmpl | 10 +--- plugins/dashboard/plugin.go | 8 +++ 10 files changed, 134 insertions(+), 68 deletions(-) create mode 100644 packages/dashboard/metrics_chain.go create mode 100644 packages/dashboard/metrics_chain_nodeconn.go delete mode 100644 packages/dashboard/metrics_nodeconn_messages.go create mode 100644 packages/dashboard/templates/metrics_chain.tmpl rename packages/dashboard/templates/{metrics_nodeconn_messages.tmpl => metrics_chain_nodeconn.tmpl} (100%) diff --git a/packages/dashboard/metrics.go b/packages/dashboard/metrics.go index c160cde263..cac1d4d94c 100644 --- a/packages/dashboard/metrics.go +++ b/packages/dashboard/metrics.go @@ -13,7 +13,8 @@ var tplMetrics string func (d *Dashboard) metricsInit(e *echo.Echo, r renderer) Tab { ret := d.initMetrics(e, r) d.initMetricsNodeconn(e, r) - d.initMetricsNodeconnMessages(e, r) + d.initMetricsChain(e, r) + d.initMetricsChainNodeconn(e, r) return ret } @@ -31,11 +32,17 @@ func (d *Dashboard) initMetrics(e *echo.Echo, r renderer) Tab { } func (d *Dashboard) handleMetrics(c echo.Context) error { + chains, err := d.fetchChains() + if err != nil { + return err + } return c.Render(http.StatusOK, c.Path(), &MetricsTemplateParams{ BaseTemplateParams: d.BaseParams(c), + Chains: chains, }) } type MetricsTemplateParams struct { BaseTemplateParams + Chains []*ChainOverview } diff --git a/packages/dashboard/metrics_chain.go b/packages/dashboard/metrics_chain.go new file mode 100644 index 0000000000..5252ece1ac --- /dev/null +++ b/packages/dashboard/metrics_chain.go @@ -0,0 +1,44 @@ +package dashboard + +import ( + _ "embed" + "fmt" + "net/http" + + "github.com/iotaledger/wasp/packages/iscp" + "github.com/labstack/echo/v4" +) + +//go:embed templates/metrics_chain.tmpl +var tplMetricsChain string + +func metricsChainBreadcrumb(e *echo.Echo, chainID *iscp.ChainID) Tab { + return Tab{ + Path: e.Reverse("metricsChain"), + Title: fmt.Sprintf("Metrics: %.8s", chainID.Base58()), + Href: e.Reverse("metricsChain", chainID.Base58()), + } +} + +func (d *Dashboard) initMetricsChain(e *echo.Echo, r renderer) { + route := e.GET("/metrics/:chainid", d.handleMetricsChain) + route.Name = "metricsChain" + r[route.Path] = d.makeTemplate(e, tplMetricsChain) +} + +func (d *Dashboard) handleMetricsChain(c echo.Context) error { + chainID, err := iscp.ChainIDFromBase58(c.Param("chainid")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err) + } + tab := metricsChainBreadcrumb(c.Echo(), chainID) + return c.Render(http.StatusOK, c.Path(), &MetricsChainTemplateParams{ + BaseTemplateParams: d.BaseParams(c, tab), + ChainID: chainID.Base58(), + }) +} + +type MetricsChainTemplateParams struct { + BaseTemplateParams + ChainID string +} diff --git a/packages/dashboard/metrics_chain_nodeconn.go b/packages/dashboard/metrics_chain_nodeconn.go new file mode 100644 index 0000000000..2433ff3a2d --- /dev/null +++ b/packages/dashboard/metrics_chain_nodeconn.go @@ -0,0 +1,49 @@ +package dashboard + +import ( + _ "embed" + "fmt" + "net/http" + + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/metrics/nodeconnmetrics" + "github.com/labstack/echo/v4" +) + +//go:embed templates/metrics_chain_nodeconn.tmpl +var tplMetricsChainNodeconn string + +func metricsChainNodeconnBreadcrumb(e *echo.Echo, chainID *iscp.ChainID) Tab { + return Tab{ + Path: e.Reverse("metricsChainNodeconn"), + Title: fmt.Sprintf("Metrics: %.8s: Connection to L1", chainID.Base58()), + Href: e.Reverse("metricsChainNodeconn", chainID.Base58()), + } +} + +func (d *Dashboard) initMetricsChainNodeconn(e *echo.Echo, r renderer) { + route := e.GET("/metrics/:chainid/nodeconn", d.handleMetricsChainNodeconn) + route.Name = "metricsChainNodeconn" + r[route.Path] = d.makeTemplate(e, tplMetricsChainNodeconn) +} + +func (d *Dashboard) handleMetricsChainNodeconn(c echo.Context) error { + chainID, err := iscp.ChainIDFromBase58(c.Param("chainid")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err) + } + tab := metricsChainNodeconnBreadcrumb(c.Echo(), chainID) + metrics, err := d.wasp.GetChainNodeConnectionMetrics(chainID) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err) + } + return c.Render(http.StatusOK, c.Path(), &MetricsChainNodeconnTemplateParams{ + BaseTemplateParams: d.BaseParams(c, metricsChainBreadcrumb(c.Echo(), chainID), tab), + Metrics: metrics, + }) +} + +type MetricsChainNodeconnTemplateParams struct { + BaseTemplateParams + Metrics nodeconnmetrics.NodeConnectionMessagesMetrics +} diff --git a/packages/dashboard/metrics_nodeconn.go b/packages/dashboard/metrics_nodeconn.go index 8c1b0b5ace..ffbbf9eb03 100644 --- a/packages/dashboard/metrics_nodeconn.go +++ b/packages/dashboard/metrics_nodeconn.go @@ -22,14 +22,10 @@ func metricsNodeconnBreadcrumb(e *echo.Echo) Tab { func (d *Dashboard) initMetricsNodeconn(e *echo.Echo, r renderer) { route := e.GET("/metrics/nodeconn", d.handleMetricsNodeconn) route.Name = "metricsNodeconn" - r[route.Path] = d.makeTemplate(e, tplMetricsNodeconnMessages, tplMetricsNodeconn) + r[route.Path] = d.makeTemplate(e, tplMetricsChainNodeconn, tplMetricsNodeconn) } func (d *Dashboard) handleMetricsNodeconn(c echo.Context) error { - chains, err := d.fetchChains() - if err != nil { - return err - } metrics, err := d.wasp.GetNodeConnectionMetrics() if err != nil { return echo.NewHTTPError(http.StatusBadRequest, err) @@ -37,13 +33,11 @@ func (d *Dashboard) handleMetricsNodeconn(c echo.Context) error { tab := metricsNodeconnBreadcrumb(c.Echo()) return c.Render(http.StatusOK, c.Path(), &MetricsNodeconnTemplateParams{ BaseTemplateParams: d.BaseParams(c, tab), - Chains: chains, Metrics: metrics, }) } type MetricsNodeconnTemplateParams struct { BaseTemplateParams - Chains []*ChainOverview Metrics nodeconnmetrics.NodeConnectionMetrics } diff --git a/packages/dashboard/metrics_nodeconn_messages.go b/packages/dashboard/metrics_nodeconn_messages.go deleted file mode 100644 index b4691b26b2..0000000000 --- a/packages/dashboard/metrics_nodeconn_messages.go +++ /dev/null @@ -1,49 +0,0 @@ -package dashboard - -import ( - _ "embed" - "fmt" - "net/http" - - "github.com/iotaledger/wasp/packages/iscp" - "github.com/iotaledger/wasp/packages/metrics/nodeconnmetrics" - "github.com/labstack/echo/v4" -) - -//go:embed templates/metrics_nodeconn_messages.tmpl -var tplMetricsNodeconnMessages string - -func metricsNodeconnMessagesBreadcrumb(e *echo.Echo, chainID *iscp.ChainID) Tab { - return Tab{ - Path: e.Reverse("metricsNodeconnMessages"), - Title: fmt.Sprintf("Metrics: Connection to L1: %.8s", chainID.Base58()), - Href: e.Reverse("metricsNodeconnMessages", chainID.Base58()), - } -} - -func (d *Dashboard) initMetricsNodeconnMessages(e *echo.Echo, r renderer) { - route := e.GET("/metrics/nodeconn/:chainid", d.handleMetricsNodeconnMessages) - route.Name = "metricsNodeconnMessages" - r[route.Path] = d.makeTemplate(e, tplMetricsNodeconnMessages) -} - -func (d *Dashboard) handleMetricsNodeconnMessages(c echo.Context) error { - chainID, err := iscp.ChainIDFromBase58(c.Param("chainid")) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, err) - } - tab := metricsNodeconnMessagesBreadcrumb(c.Echo(), chainID) - metrics, err := d.wasp.GetChainNodeConnectionMetrics(chainID) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, err) - } - return c.Render(http.StatusOK, c.Path(), &MetricsNodeconnMessagesTemplateParams{ - BaseTemplateParams: d.BaseParams(c, metricsNodeconnBreadcrumb(c.Echo()), tab), - Metrics: metrics, - }) -} - -type MetricsNodeconnMessagesTemplateParams struct { - BaseTemplateParams - Metrics nodeconnmetrics.NodeConnectionMessagesMetrics -} diff --git a/packages/dashboard/templates/metrics.tmpl b/packages/dashboard/templates/metrics.tmpl index ccf0dca056..13f7618b3a 100644 --- a/packages/dashboard/templates/metrics.tmpl +++ b/packages/dashboard/templates/metrics.tmpl @@ -2,10 +2,21 @@ {{define "body"}}
    -

    Metrics

    - +

    Global metrics

    + +

    Chain metrics

    +
      + {{range $_, $c := .Chains}} + {{ $id := $c.ChainRecord.ChainID.Base58 }} +
    • {{ $id }} + +
    • + {{end}} +
    {{end}} diff --git a/packages/dashboard/templates/metrics_chain.tmpl b/packages/dashboard/templates/metrics_chain.tmpl new file mode 100644 index 0000000000..3370360084 --- /dev/null +++ b/packages/dashboard/templates/metrics_chain.tmpl @@ -0,0 +1,10 @@ +{{define "title"}}Chain metrics{{end}} + +{{define "body"}} +
    +

    {{.ChainID}}

    + +
    +{{end}} diff --git a/packages/dashboard/templates/metrics_nodeconn_messages.tmpl b/packages/dashboard/templates/metrics_chain_nodeconn.tmpl similarity index 100% rename from packages/dashboard/templates/metrics_nodeconn_messages.tmpl rename to packages/dashboard/templates/metrics_chain_nodeconn.tmpl diff --git a/packages/dashboard/templates/metrics_nodeconn.tmpl b/packages/dashboard/templates/metrics_nodeconn.tmpl index 6c74a95966..e4f8d75086 100644 --- a/packages/dashboard/templates/metrics_nodeconn.tmpl +++ b/packages/dashboard/templates/metrics_nodeconn.tmpl @@ -2,19 +2,11 @@ {{define "body"}}
    -

    All the chains

    -
      - {{range $_, $c := .Chains}} - {{ $id := $c.ChainRecord.ChainID.Base58 }} -
    • {{ $id }}
    • - {{end}} -
    -

    Chains, subscribed to L1

      {{range $_, $addr := (.Metrics.GetSubscribed)}} {{ $id := $addr.Base58 }} -
    • {{ $id }}
    • +
    • {{ $id }}
    • {{end}}
    diff --git a/plugins/dashboard/plugin.go b/plugins/dashboard/plugin.go index ce99ef1522..e0c703b325 100644 --- a/plugins/dashboard/plugin.go +++ b/plugins/dashboard/plugin.go @@ -123,6 +123,14 @@ func (w *waspServices) GetNodeConnectionMetrics() (nodeconnmetrics.NodeConnectio return chs.GetNodeConnectionMetrics(), nil } +func (w *waspServices) GetChainConsensusWorkflowStatus(chainID *iscp.ChainID) (chain.ConsensusWorkflowStatus, error) { + ch := chains.AllChains().Get(chainID) + if ch == nil { + return nil, echo.NewHTTPError(http.StatusNotFound, "Chain not found") + } + return ch.GetConsensusWorkflowStatus(), nil +} + func (w *waspServices) CallView(chainID *iscp.ChainID, scName, funName string, params dict.Dict) (dict.Dict, error) { ch := chains.AllChains().Get(chainID) if ch == nil { From 3d9bc11ca4d20712cbc666d8d4249efacb274181 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Tue, 11 Jan 2022 18:19:51 +0200 Subject: [PATCH 101/120] Handle error in a nicer way. --- packages/registry/registry_impl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/registry/registry_impl.go b/packages/registry/registry_impl.go index bb48c3cfdf..f7f894659a 100644 --- a/packages/registry/registry_impl.go +++ b/packages/registry/registry_impl.go @@ -133,7 +133,7 @@ func (r *Impl) SaveDKShare(dkShare *tcrypto.DKShare) error { func (r *Impl) LoadDKShare(sharedAddress ledgerstate.Address) (*tcrypto.DKShare, error) { data, err := r.store.Get(dbKeyForDKShare(sharedAddress)) if err != nil { - if err.Error() == "key not found" { + if errors.Is(err, kvstore.ErrKeyNotFound) { return nil, ErrDKShareNotFound } return nil, err From 31194de9f2556444ce7ba90542eb130c3b87163b Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Tue, 11 Jan 2022 18:20:14 +0200 Subject: [PATCH 102/120] Dashboard chain metric list made into a template --- packages/dashboard/metrics.go | 2 +- packages/dashboard/templates/metrics.tmpl | 7 +++---- packages/dashboard/templates/metrics_chain.tmpl | 11 ++++++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/dashboard/metrics.go b/packages/dashboard/metrics.go index cac1d4d94c..db7ec72222 100644 --- a/packages/dashboard/metrics.go +++ b/packages/dashboard/metrics.go @@ -22,7 +22,7 @@ func (d *Dashboard) initMetrics(e *echo.Echo, r renderer) Tab { route := e.GET("/metrics", d.handleMetrics) route.Name = "metrics" - r[route.Path] = d.makeTemplate(e, tplMetrics) + r[route.Path] = d.makeTemplate(e, tplMetricsChain, tplMetrics) return Tab{ Path: route.Path, diff --git a/packages/dashboard/templates/metrics.tmpl b/packages/dashboard/templates/metrics.tmpl index 13f7618b3a..8c5c429eb5 100644 --- a/packages/dashboard/templates/metrics.tmpl +++ b/packages/dashboard/templates/metrics.tmpl @@ -11,10 +11,9 @@
      {{range $_, $c := .Chains}} {{ $id := $c.ChainRecord.ChainID.Base58 }} -
    • {{ $id }} - +
    • + {{ $id }} + {{template "metricsChainAll" (args $id)}}
    • {{end}}
    diff --git a/packages/dashboard/templates/metrics_chain.tmpl b/packages/dashboard/templates/metrics_chain.tmpl index 3370360084..100cfa9ce7 100644 --- a/packages/dashboard/templates/metrics_chain.tmpl +++ b/packages/dashboard/templates/metrics_chain.tmpl @@ -1,10 +1,15 @@ +{{define "metricsChainAll"}} + {{ $id := (index . 0) }} + +{{end}} + {{define "title"}}Chain metrics{{end}} {{define "body"}}

    {{.ChainID}}

    - + {{template "metricsChainAll" (args .ChainID)}}
    {{end}} From c663914edc959f2b071316cc3f55b32d3c3ff720 Mon Sep 17 00:00:00 2001 From: Diego Essaya Date: Tue, 11 Jan 2022 13:46:26 -0300 Subject: [PATCH 103/120] [fix] (goshimmer client) GetConfirmedOutputs(): filter out rejected outputs --- client/goshimmer/goshimmer.go | 38 ++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/client/goshimmer/goshimmer.go b/client/goshimmer/goshimmer.go index 4ea6ac5df5..8315e974d2 100644 --- a/client/goshimmer/goshimmer.go +++ b/client/goshimmer/goshimmer.go @@ -69,17 +69,49 @@ func (c *Client) GetConfirmedOutputs(address ledgerstate.Address) ([]ledgerstate if err != nil { return nil, fmt.Errorf("GetUnspentOutputs: %w", err) } - ret := make([]ledgerstate.Output, len(r.Outputs)) - for i, out := range r.Outputs { + + // prevent calling c.IsTransactionConfirmed() twice for the same tx + confirmedCache := map[string]bool{} + isConfirmed := func(txID string) (bool, error) { + confirmed, ok := confirmedCache[txID] + if ok { + return confirmed, nil + } + confirmed, err = c.IsTransactionConfirmed(txID) + if err != nil { + return false, err + } + confirmedCache[txID] = confirmed + return confirmed, nil + } + + var ret []ledgerstate.Output + for _, out := range r.Outputs { var err error - ret[i], err = out.ToLedgerstateOutput() + confirmed, err := isConfirmed(out.OutputID.TransactionID) + if err != nil { + return nil, err + } + if !confirmed { + continue + } + output, err := out.ToLedgerstateOutput() if err != nil { return nil, err } + ret = append(ret, output) } return ret, nil } +func (c *Client) IsTransactionConfirmed(txID string) (bool, error) { + r, err := c.api.GetTransactionInclusionState(txID) + if err != nil { + return false, fmt.Errorf("IsTransactionConfirmed: %w", err) + } + return r.Confirmed && !r.Rejected, nil +} + func (c *Client) postTx(tx *ledgerstate.Transaction) error { data := tx.Bytes() if len(data) > parameters.MaxSerializedTransactionToGoshimmer { From 5aee33ed9bcc3c80c90a1d3cf10f22c1adad02d4 Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Wed, 12 Jan 2022 10:06:57 +0200 Subject: [PATCH 104/120] Some consensus workflow status is present in dashboard --- packages/dashboard/base.go | 1 + packages/dashboard/metrics.go | 1 + packages/dashboard/metrics_chain_consensus.go | 51 +++++++++++++++++++ packages/dashboard/metrics_chain_nodeconn.go | 2 + packages/dashboard/mock_test.go | 6 +++ .../dashboard/templates/metrics_chain.tmpl | 1 + .../templates/metrics_chain_consensus.tmpl | 38 ++++++++++++++ packages/dashboard/util.go | 4 +- 8 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 packages/dashboard/metrics_chain_consensus.go create mode 100644 packages/dashboard/templates/metrics_chain_consensus.tmpl diff --git a/packages/dashboard/base.go b/packages/dashboard/base.go index d6b3f477e3..0248348832 100644 --- a/packages/dashboard/base.go +++ b/packages/dashboard/base.go @@ -48,6 +48,7 @@ type WaspServices interface { CallView(chainID *iscp.ChainID, scName, fname string, params dict.Dict) (dict.Dict, error) GetChainNodeConnectionMetrics(*iscp.ChainID) (nodeconnmetrics.NodeConnectionMessagesMetrics, error) GetNodeConnectionMetrics() (nodeconnmetrics.NodeConnectionMetrics, error) + GetChainConsensusWorkflowStatus(*iscp.ChainID) (chain.ConsensusWorkflowStatus, error) } type Dashboard struct { diff --git a/packages/dashboard/metrics.go b/packages/dashboard/metrics.go index db7ec72222..acb2546b60 100644 --- a/packages/dashboard/metrics.go +++ b/packages/dashboard/metrics.go @@ -15,6 +15,7 @@ func (d *Dashboard) metricsInit(e *echo.Echo, r renderer) Tab { d.initMetricsNodeconn(e, r) d.initMetricsChain(e, r) d.initMetricsChainNodeconn(e, r) + d.initMetricsChainConsensus(e, r) return ret } diff --git a/packages/dashboard/metrics_chain_consensus.go b/packages/dashboard/metrics_chain_consensus.go new file mode 100644 index 0000000000..e1de2085fb --- /dev/null +++ b/packages/dashboard/metrics_chain_consensus.go @@ -0,0 +1,51 @@ +package dashboard + +import ( + _ "embed" + "fmt" + "net/http" + + "github.com/iotaledger/wasp/packages/chain" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/labstack/echo/v4" +) + +//go:embed templates/metrics_chain_consensus.tmpl +var tplMetricsChainConsensus string + +func metricsChainConsensusBreadcrumb(e *echo.Echo, chainID *iscp.ChainID) Tab { + return Tab{ + Path: e.Reverse("metricsChainConsensus"), + Title: fmt.Sprintf("Metrics: %.8s: Consensus", chainID.Base58()), + Href: e.Reverse("metricsChainConsensus", chainID.Base58()), + } +} + +func (d *Dashboard) initMetricsChainConsensus(e *echo.Echo, r renderer) { + route := e.GET("/metrics/:chainid/consensus", d.handleMetricsChainConsensus) + route.Name = "metricsChainConsensus" + r[route.Path] = d.makeTemplate(e, tplMetricsChainConsensus) +} + +func (d *Dashboard) handleMetricsChainConsensus(c echo.Context) error { + chainID, err := iscp.ChainIDFromBase58(c.Param("chainid")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err) + } + tab := metricsChainConsensusBreadcrumb(c.Echo(), chainID) + status, err := d.wasp.GetChainConsensusWorkflowStatus(chainID) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err) + } + return c.Render(http.StatusOK, c.Path(), &MetricsChainConsensusTemplateParams{ + BaseTemplateParams: d.BaseParams(c, metricsChainBreadcrumb(c.Echo(), chainID), tab), + ChainID: chainID.Base58(), + Status: status, + }) +} + +type MetricsChainConsensusTemplateParams struct { + BaseTemplateParams + ChainID string + Status chain.ConsensusWorkflowStatus +} diff --git a/packages/dashboard/metrics_chain_nodeconn.go b/packages/dashboard/metrics_chain_nodeconn.go index 2433ff3a2d..3bcb0c6c1c 100644 --- a/packages/dashboard/metrics_chain_nodeconn.go +++ b/packages/dashboard/metrics_chain_nodeconn.go @@ -39,11 +39,13 @@ func (d *Dashboard) handleMetricsChainNodeconn(c echo.Context) error { } return c.Render(http.StatusOK, c.Path(), &MetricsChainNodeconnTemplateParams{ BaseTemplateParams: d.BaseParams(c, metricsChainBreadcrumb(c.Echo(), chainID), tab), + ChainID: chainID.Base58(), Metrics: metrics, }) } type MetricsChainNodeconnTemplateParams struct { BaseTemplateParams + ChainID string Metrics nodeconnmetrics.NodeConnectionMessagesMetrics } diff --git a/packages/dashboard/mock_test.go b/packages/dashboard/mock_test.go index 6a67a13cc4..337eee1973 100644 --- a/packages/dashboard/mock_test.go +++ b/packages/dashboard/mock_test.go @@ -26,6 +26,8 @@ type waspServicesMock struct { chains map[[ledgerstate.AddressLength]byte]*solo.Chain } +var _ WaspServices = &waspServicesMock{} + func (w *waspServicesMock) ConfigDump() map[string]interface{} { return map[string]interface{}{ "foo": "bar", @@ -134,6 +136,10 @@ func (w *waspServicesMock) GetNodeConnectionMetrics() (nodeconnmetrics.NodeConne panic("Not implemented") } +func (w *waspServicesMock) GetChainConsensusWorkflowStatus(chainID *iscp.ChainID) (chain.ConsensusWorkflowStatus, error) { + panic("Not implemented") +} + type dashboardTestEnv struct { wasp *waspServicesMock echo *echo.Echo diff --git a/packages/dashboard/templates/metrics_chain.tmpl b/packages/dashboard/templates/metrics_chain.tmpl index 100cfa9ce7..0fc3de64be 100644 --- a/packages/dashboard/templates/metrics_chain.tmpl +++ b/packages/dashboard/templates/metrics_chain.tmpl @@ -2,6 +2,7 @@ {{ $id := (index . 0) }} {{end}} diff --git a/packages/dashboard/templates/metrics_chain_consensus.tmpl b/packages/dashboard/templates/metrics_chain_consensus.tmpl new file mode 100644 index 0000000000..c4bd544b54 --- /dev/null +++ b/packages/dashboard/templates/metrics_chain_consensus.tmpl @@ -0,0 +1,38 @@ +{{define "title"}}Consensus status{{end}} + +{{define "body"}} +
    +

    Workflow

    +
    IndexIDPubKeyNetID Status
    {{$s.Index}}{{$s.PeeringID}}{{$s.PubKey.String}}{{$s.NetID}} {{if $s.Connected}}up{{else}}down{{end}}
    + + + + + + + + + + + + + + + + + + + +
    Flag nameStatusTriger time
    State received + +
    Batch proposal sent{{ (formatTimestampOrNever (.Status.GetBatchProposalSentTime)) }}
    +

    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 U;4Cu}NGyehy7u;rZPvd31s$yst^fc4 literal 645023 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ℑZDjm}DIF+JoMMa7ivZ;>^o^O^6 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

+{{end}} diff --git a/packages/dashboard/util.go b/packages/dashboard/util.go index 35267ee95d..0b9282e724 100644 --- a/packages/dashboard/util.go +++ b/packages/dashboard/util.go @@ -64,8 +64,8 @@ func formatTimestamp(ts interface{}) string { } func formatTimestampOrNever(t time.Time) string { - bla := time.Time{} - if t == bla { + timestampNever := time.Time{} + if t == timestampNever { return "NEVER" } return formatTimestamp(t) From 4972f18d4d4836a2c6ab597c90927c5df47e83c0 Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Wed, 12 Jan 2022 10:14:29 +0200 Subject: [PATCH 105/120] Boolean value display in dashboard made into template --- packages/dashboard/templates/base.tmpl | 8 ++++++++ .../templates/metrics_chain_consensus.tmpl | 14 ++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/dashboard/templates/base.tmpl b/packages/dashboard/templates/base.tmpl index 449f2fe346..608c1f6975 100644 --- a/packages/dashboard/templates/base.tmpl +++ b/packages/dashboard/templates/base.tmpl @@ -44,6 +44,14 @@ {{ trim 100 (replace $longString "\n" "" -1) }} {{end}} +{{define "booleanValue"}} + +{{end}} + {{define "base"}} diff --git a/packages/dashboard/templates/metrics_chain_consensus.tmpl b/packages/dashboard/templates/metrics_chain_consensus.tmpl index c4bd544b54..dd06138d5e 100644 --- a/packages/dashboard/templates/metrics_chain_consensus.tmpl +++ b/packages/dashboard/templates/metrics_chain_consensus.tmpl @@ -14,22 +14,12 @@ State received - - - + {{ template "booleanValue" (.Status.IsStateReceived) }} Batch proposal sent - + {{ template "booleanValue" (.Status.IsBatchProposalSent) }} {{ (formatTimestampOrNever (.Status.GetBatchProposalSentTime)) }} From 306e967b4ff3c172e3aeced1cb9e9e6e9ff3374e Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Wed, 12 Jan 2022 10:20:09 +0200 Subject: [PATCH 106/120] Consensus flag inverted in cli --- tools/wasp-cli/metrics/consensus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/wasp-cli/metrics/consensus.go b/tools/wasp-cli/metrics/consensus.go index 506e6a767f..07dad27713 100644 --- a/tools/wasp-cli/metrics/consensus.go +++ b/tools/wasp-cli/metrics/consensus.go @@ -32,7 +32,7 @@ var consensusMetricsCmd = &cobra.Command{ table[5] = makeWorkflowTableRow("Transaction finalized", workflowStatus.FlagTransactionFinalized, workflowStatus.TimeTransactionFinalized) table[6] = makeWorkflowTableRow("Transaction posted to L1", workflowStatus.FlagTransactionPosted, workflowStatus.TimeTransactionPosted) // TODO: is not meaningful, if I am not a contributor table[7] = makeWorkflowTableRow("Transaction seen by L1", workflowStatus.FlagTransactionSeen, workflowStatus.TimeTransactionSeen) - table[8] = makeWorkflowTableRow("Consensus in progress", workflowStatus.FlagInProgress, workflowStatus.TimeCompleted) + table[8] = makeWorkflowTableRow("Consensus is completed", !(workflowStatus.FlagInProgress), workflowStatus.TimeCompleted) log.PrintTable(header, table) }, } From 214410564c6a714e8c4b95575247d8d13655c734 Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Wed, 12 Jan 2022 10:28:01 +0200 Subject: [PATCH 107/120] All consensus workflow flags are displayed in dashboard --- .../templates/metrics_chain_consensus.tmpl | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/dashboard/templates/metrics_chain_consensus.tmpl b/packages/dashboard/templates/metrics_chain_consensus.tmpl index dd06138d5e..ca40c2d5c7 100644 --- a/packages/dashboard/templates/metrics_chain_consensus.tmpl +++ b/packages/dashboard/templates/metrics_chain_consensus.tmpl @@ -22,6 +22,41 @@ {{ template "booleanValue" (.Status.IsBatchProposalSent) }} {{ (formatTimestampOrNever (.Status.GetBatchProposalSentTime)) }} + + Consensus on batch reached + {{ template "booleanValue" (.Status.IsConsensusBatchKnown) }} + {{ (formatTimestampOrNever (.Status.GetConsensusBatchKnownTime)) }} + + + Virtual machine started + {{ template "booleanValue" (.Status.IsVMStarted) }} + {{ (formatTimestampOrNever (.Status.GetVMStartedTime)) }} + + + Virtual machine result signed + {{ template "booleanValue" (.Status.IsVMResultSigned) }} + {{ (formatTimestampOrNever (.Status.GetVMResultSignedTime)) }} + + + Transaction finalized + {{ template "booleanValue" (.Status.IsTransactionFinalized) }} + {{ (formatTimestampOrNever (.Status.GetTransactionFinalizedTime)) }} + + + Transaction posted to L1 + {{ template "booleanValue" (.Status.IsTransactionPosted) }} + {{ (formatTimestampOrNever (.Status.GetTransactionPostedTime)) }} + + + Transaction seen by L1 + {{ template "booleanValue" (.Status.IsTransactionSeen) }} + {{ (formatTimestampOrNever (.Status.GetTransactionSeenTime)) }} + + + Consensus is completed + {{ template "booleanValue" (not (.Status.IsInProgress)) }} + {{ (formatTimestampOrNever (.Status.GetCompletedTime)) }} + From 73e2c729c62f2b0dd1fc69f0c58bb22377ad10c9 Mon Sep 17 00:00:00 2001 From: Julius Andrikonis Date: Wed, 12 Jan 2022 10:32:26 +0200 Subject: [PATCH 108/120] Appeasing linter --- packages/dashboard/metrics_chain_consensus.go | 1 + packages/dashboard/metrics_chain_nodeconn.go | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/dashboard/metrics_chain_consensus.go b/packages/dashboard/metrics_chain_consensus.go index e1de2085fb..41c5ab7127 100644 --- a/packages/dashboard/metrics_chain_consensus.go +++ b/packages/dashboard/metrics_chain_consensus.go @@ -1,3 +1,4 @@ +//nolint:dupl package dashboard import ( diff --git a/packages/dashboard/metrics_chain_nodeconn.go b/packages/dashboard/metrics_chain_nodeconn.go index 3bcb0c6c1c..b1c0cd88bc 100644 --- a/packages/dashboard/metrics_chain_nodeconn.go +++ b/packages/dashboard/metrics_chain_nodeconn.go @@ -1,3 +1,4 @@ +//nolint:dupl package dashboard import ( From e82c5630c0cce371d9f777b2668ed2f16043b706 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 19 Jan 2022 15:24:19 +0200 Subject: [PATCH 109/120] Metrics: Chain now has current_state_index metric. --- packages/chain/chainimpl/chainimpl.go | 1 + packages/metrics/chain.go | 7 +++++++ packages/metrics/metrics.go | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index 9eaecdd551..dd1657e388 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -269,6 +269,7 @@ func (c *chainObj) processChainTransition(msg *chain.ChainTransitionEventData) { c.mempoolLastCleanedIndex = stateIndex c.updateChainNodes(stateIndex) + c.chainMetrics.CurrentStateIndex(stateIndex) } else { c.log.Debugf("processChainTransition state %d: output %s is governance updated; state hash %s", stateIndex, iscp.OID(msg.ChainOutput.ID()), msg.VirtualState.StateCommitment().String()) diff --git a/packages/metrics/chain.go b/packages/metrics/chain.go index 338d627c14..4e89d088ee 100644 --- a/packages/metrics/chain.go +++ b/packages/metrics/chain.go @@ -15,6 +15,7 @@ type StateManagerMetrics interface { type ChainMetrics interface { CountMessages() CountRequestAckMessages() + CurrentStateIndex(stateIndex uint32) MempoolMetrics ConsensusMetrics StateManagerMetrics @@ -63,6 +64,10 @@ func (c *chainMetricsObj) CountRequestAckMessages() { c.metrics.requestAckMessages.With(prometheus.Labels{"chain": c.chainID.String()}).Inc() } +func (c *chainMetricsObj) CurrentStateIndex(stateIndex uint32) { + c.metrics.currentStateIndex.With(prometheus.Labels{"chain": c.chainID.String()}).Set(float64(stateIndex)) +} + func (c *chainMetricsObj) RecordRequestProcessingTime(reqID iscp.RequestID, elapse time.Duration) { c.metrics.requestProcessingTime.With(prometheus.Labels{"chain": c.chainID.String(), "request": reqID.String()}).Set(elapse.Seconds()) } @@ -99,6 +104,8 @@ func (m *defaultChainMetrics) CountMessages() {} func (m *defaultChainMetrics) CountRequestAckMessages() {} +func (m *defaultChainMetrics) CurrentStateIndex(stateIndex uint32) {} + func (m *defaultChainMetrics) RecordRequestProcessingTime(_ iscp.RequestID, _ time.Duration) {} func (m *defaultChainMetrics) RecordVMRunTime(_ time.Duration) {} diff --git a/packages/metrics/metrics.go b/packages/metrics/metrics.go index a40f1e6bf6..a4d40c85a3 100644 --- a/packages/metrics/metrics.go +++ b/packages/metrics/metrics.go @@ -23,6 +23,7 @@ type Metrics struct { processedRequestCounter *prometheus.CounterVec messagesReceived *prometheus.CounterVec requestAckMessages *prometheus.CounterVec + currentStateIndex *prometheus.GaugeVec requestProcessingTime *prometheus.GaugeVec vmRunTime *prometheus.GaugeVec vmRunCounter *prometheus.CounterVec @@ -109,6 +110,12 @@ func (m *Metrics) registerMetrics() { }, []string{"chain"}) prometheus.MustRegister(m.requestAckMessages) + m.currentStateIndex = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "current_state_index", + Help: "The current chain state index.", + }, []string{"chain"}) + prometheus.MustRegister(m.currentStateIndex) + m.requestProcessingTime = prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: "request_processing_time", Help: "Time to process request", From 3ea3d051aea55393cb6001c887fa484548dba755 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 19 Jan 2022 17:46:08 +0200 Subject: [PATCH 110/120] Metrics: last_seen_state_index metric added to the state manager. --- config.json | 4 ++++ packages/chain/statemgr/eventproc.go | 1 + packages/chain/statemgr/setup_test.go | 2 ++ packages/metrics/chain.go | 11 +++++++++++ packages/metrics/metrics.go | 8 ++++++++ 5 files changed, 26 insertions(+) diff --git a/config.json b/config.json index c346d1c406..afd7e70510 100644 --- a/config.json +++ b/config.json @@ -41,5 +41,9 @@ }, "nanomsg":{ "port": 5550 + }, + "metrics":{ + "bindAddress": "127.0.0.1:2112", + "enabled": true } } diff --git a/packages/chain/statemgr/eventproc.go b/packages/chain/statemgr/eventproc.go index cba11f8ab1..afcc9c5bf0 100644 --- a/packages/chain/statemgr/eventproc.go +++ b/packages/chain/statemgr/eventproc.go @@ -105,6 +105,7 @@ func (sm *stateManager) handleStateMsg(msg *messages.StateMsg) { "state index", msg.ChainOutput.GetStateIndex(), "chainOutput", iscp.OID(msg.ChainOutput.ID()), ) + sm.stateManagerMetrics.LastSeenStateIndex(msg.ChainOutput.GetStateIndex()) stateHash, err := hashing.HashValueFromBytes(msg.ChainOutput.GetStateData()) if err != nil { sm.log.Errorf("EventStateMsg ignored: failed to parse state hash: %v", err) diff --git a/packages/chain/statemgr/setup_test.go b/packages/chain/statemgr/setup_test.go index 648106642c..ef33b26ebf 100644 --- a/packages/chain/statemgr/setup_test.go +++ b/packages/chain/statemgr/setup_test.go @@ -67,6 +67,8 @@ type MockedStateManagerMetrics struct{} func (c *MockedStateManagerMetrics) RecordBlockSize(_ uint32, _ float64) {} +func (c *MockedStateManagerMetrics) LastSeenStateIndex(_ uint32) {} + func NewMockedEnv(nodeCount int, t *testing.T, debug bool) (*MockedEnv, *ledgerstate.Transaction) { level := zapcore.InfoLevel if debug { diff --git a/packages/metrics/chain.go b/packages/metrics/chain.go index 4e89d088ee..32ec40e271 100644 --- a/packages/metrics/chain.go +++ b/packages/metrics/chain.go @@ -10,6 +10,7 @@ import ( type StateManagerMetrics interface { RecordBlockSize(blockIndex uint32, size float64) + LastSeenStateIndex(stateIndex uint32) } type ChainMetrics interface { @@ -88,6 +89,14 @@ func (c *chainMetricsObj) RecordBlockSize(blockIndex uint32, blockSize float64) c.metrics.blockSizes.With(prometheus.Labels{"chain": c.chainID.String(), "block_index": fmt.Sprintf("%d", blockIndex)}).Set(blockSize) } +func (c *chainMetricsObj) LastSeenStateIndex(stateIndex uint32) { + if c.metrics.lastSeenStateIndexVal >= stateIndex { + return + } + c.metrics.lastSeenStateIndexVal = stateIndex + c.metrics.lastSeenStateIndex.With(prometheus.Labels{"chain": c.chainID.String()}).Set(float64(stateIndex)) +} + type defaultChainMetrics struct{} func DefaultChainMetrics() ChainMetrics { @@ -115,3 +124,5 @@ func (m *defaultChainMetrics) CountVMRuns() {} func (m *defaultChainMetrics) CountBlocksPerChain() {} func (m *defaultChainMetrics) RecordBlockSize(_ uint32, _ float64) {} + +func (m *defaultChainMetrics) LastSeenStateIndex(stateIndex uint32) {} diff --git a/packages/metrics/metrics.go b/packages/metrics/metrics.go index a4d40c85a3..c5b6de6cfd 100644 --- a/packages/metrics/metrics.go +++ b/packages/metrics/metrics.go @@ -29,6 +29,8 @@ type Metrics struct { vmRunCounter *prometheus.CounterVec blocksPerChain *prometheus.CounterVec blockSizes *prometheus.GaugeVec + lastSeenStateIndex *prometheus.GaugeVec + lastSeenStateIndexVal uint32 nodeconnMetrics nodeconnmetrics.NodeConnectionMetrics } @@ -145,6 +147,12 @@ func (m *Metrics) registerMetrics() { Help: "Block sizes", }, []string{"block_index", "chain"}) prometheus.MustRegister(m.blockSizes) + + m.lastSeenStateIndex = prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "last_seen_state_index", + Help: "Last seen state index", + }, []string{"chain"}) + prometheus.MustRegister(m.lastSeenStateIndex) } func (m *Metrics) GetNodeConnectionMetrics() nodeconnmetrics.NodeConnectionMetrics { From cb8a9dd50cca188bbce6cc064033a3a0d4609ff2 Mon Sep 17 00:00:00 2001 From: Erno Aapa Date: Mon, 24 Jan 2022 13:14:27 +0200 Subject: [PATCH 111/120] Fix goreleaser not found in release pipeline The release GitHub actions release pipeline was failing to error "goreleaser: command not found". The root cause was that the goreleaser tool was not installed, because at some stage the "goreleaser/goreleaser-action" were dropped off. Now back and the release should work again. --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34a058225e..a2b862e579 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,9 @@ jobs: go-version: 1.15 - name: Run GoReleaser - run: goreleaser --rm-dist + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d8b5609f36c3677724920e582490b7f927e7d212 Mon Sep 17 00:00:00 2001 From: Erno Aapa Date: Mon, 24 Jan 2022 15:11:18 +0200 Subject: [PATCH 112/120] Update release pipeline to use right Golang version The golang version in release pipeline were fixed to v1.15 but in other places, we use the latest version. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2b862e579..74e4de1bc5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.15 + go-version: ^1.15 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 From cf5a7fe1bda6dd703806600084b6efe9dd57f7e6 Mon Sep 17 00:00:00 2001 From: Billy SU Date: Tue, 25 Jan 2022 00:01:04 +0800 Subject: [PATCH 113/120] Update broken link in docs --- documentation/docs/guide/solo/what-is-solo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/guide/solo/what-is-solo.md b/documentation/docs/guide/solo/what-is-solo.md index e3661fe9a9..28fbe2c076 100644 --- a/documentation/docs/guide/solo/what-is-solo.md +++ b/documentation/docs/guide/solo/what-is-solo.md @@ -45,7 +45,7 @@ The GoLang environment with the Rust plugin is a good combination. You can find example implementations of smart contracts (including source code and tests) in the Wasp repository, in the -[contracts/rust folder](https://github.com/iotaledger/wasp/tree/master/contracts/rust). +[contracts/wasm folder](https://github.com/iotaledger/wasp/tree/master/contracts/wasm). :::tip You can find the documentation for all the functionalities available in solo in [go-docs](https://pkg.go.dev/github.com/iotaledger/wasp/packages/solo). From 4d4fbc880ba5e9c9ddf2e22a5475ff049f5370e7 Mon Sep 17 00:00:00 2001 From: Erno Aapa Date: Mon, 24 Jan 2022 20:39:09 +0200 Subject: [PATCH 114/120] Disable wasp windows and darwin binary build until its supported The grocksdb do not support darwin and windows at the moment. But support for it is already implemented but not yet released: https://github.com/gohornet/grocksdb/commit/0ad7d027aa822e322cd81b2ef7e6696b675472b6 Dosable darwin build until there's support for it. --- .goreleaser.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 4b47c9bbf8..c99d9da5da 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -12,8 +12,10 @@ builds: - -tags=builtin_static,rocksdb goos: - linux - - windows - - darwin + # Disabled until the grocksdb supports darwin and windows: + # See: https://github.com/gohornet/grocksdb/commit/0ad7d027aa822e322cd81b2ef7e6696b675472b6 + # - windows + # - darwin goarch: - amd64 @@ -39,7 +41,7 @@ archives: format: zip wrap_in_directory: true files: - - readme.md + - README.md - config.json - LICENSE From c6400446322b34d6c7d700a7586726df4db48420 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Fri, 28 Jan 2022 17:29:19 +0200 Subject: [PATCH 115/120] Use goshimmer with broken-pipe probably handled. --- go.mod | 5 +++-- go.sum | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 3033a7f605..b34c5c63e6 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,6 @@ require ( github.com/anthdm/hbbft v0.0.0-20190702061856-0826ffdcf567 github.com/bygui86/multi-profile/v2 v2.1.0 github.com/bytecodealliance/wasmtime-go v0.32.0 - github.com/wasmerio/wasmer-go v1.0.4 - github.com/second-state/WasmEdge-go v0.9.0 // indirect github.com/ethereum/go-ethereum v1.10.10 github.com/iotaledger/goshimmer v0.7.5-0.20210811162925-25c827e8326a github.com/iotaledger/hive.go v0.0.0-20210625103722-68b2cf52ef4e @@ -26,10 +24,12 @@ require ( github.com/pangpanglabs/echoswagger/v2 v2.1.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.10.0 + github.com/second-state/WasmEdge-go v0.9.0 // indirect github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.7.0 + github.com/wasmerio/wasmer-go v1.0.4 go.dedis.ch/kyber/v3 v3.0.13 go.nanomsg.org/mangos/v3 v3.0.1 go.uber.org/atomic v1.7.0 @@ -43,6 +43,7 @@ require ( replace ( github.com/anthdm/hbbft => github.com/kape1395/hbbft v0.0.0-20210824083459-b949585b7515 github.com/ethereum/go-ethereum => github.com/dessaya/go-ethereum v1.10.10-0.20211102133541-45878bcd628c + github.com/iotaledger/goshimmer => github.com/kape1395/goshimmer v0.7.5-0.20220126105741-2bc797667497 github.com/linxGnu/grocksdb => github.com/gohornet/grocksdb v1.6.38-0.20211012114404-55f425442260 go.dedis.ch/kyber/v3 v3.0.13 => github.com/kape1395/kyber/v3 v3.0.14-0.20210622094514-fefb81148dc3 ) diff --git a/go.sum b/go.sum index 3f37349bad..bccf796c22 100644 --- a/go.sum +++ b/go.sum @@ -679,6 +679,8 @@ github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4ufZiIGv2nXn4gMxnfKV306n3mWXgNu/d2TqdTU= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= +github.com/kape1395/goshimmer v0.7.5-0.20220126105741-2bc797667497 h1:Xteehyq9wIA12gi4lkvmd+VOXrgjlt9PGmm2AtOIotA= +github.com/kape1395/goshimmer v0.7.5-0.20220126105741-2bc797667497/go.mod h1:L4a9tQOOlD37go7UztwlCFMrNlmgwKK9ixaPf45A/8U= github.com/kape1395/hbbft v0.0.0-20210824083459-b949585b7515 h1:5oWqeyYqb0A+XNeM4qIESTfrgbTdCF+YTPgb9Rc50iM= github.com/kape1395/hbbft v0.0.0-20210824083459-b949585b7515/go.mod h1:XadZ7V3kvoIfFGVb8s/ndgVitoHRNRjzgPlHhhb9HlI= github.com/kape1395/kyber/v3 v3.0.14-0.20210622094514-fefb81148dc3 h1:b7VGpy3biHI8Q0O8BthxiJfOXhx5F0Ej4TNuJ9A1omo= From efed96bb20c687f96596f8635fe4403f2fbc3d82 Mon Sep 17 00:00:00 2001 From: JanOonk Date: Sun, 30 Jan 2022 12:30:31 +0100 Subject: [PATCH 116/120] Update tooling.md wrong codeblock syntax used --- documentation/docs/guide/evm/tooling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/guide/evm/tooling.md b/documentation/docs/guide/evm/tooling.md index ee957c8f09..010be525b4 100644 --- a/documentation/docs/guide/evm/tooling.md +++ b/documentation/docs/guide/evm/tooling.md @@ -56,7 +56,7 @@ If you also want to use the [Remix IDE](https://remix.ethereum.org/) to deploy a [Hardhat](https://hardhat.org/) is a commandline toolbox that allows you to deploy, test, verify, and interact with Solidity smart contracts on an EVM chain. EVM chains running on IOTA Smart Contracts are compatible with Hardhat; simply make sure you add the correct network parameters to your `hardhat.config.js`, for example: -```javascript= +```javascript networks: { local: { url: 'http://localhost:8545', From ee32536f1d861b6d3aba3d444a5961f6f4def17c Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Tue, 1 Feb 2022 23:21:08 +0530 Subject: [PATCH 117/120] renamed all contracts/rust to new dir contracts/wasm --- contracts/wasm/README.md | 4 ++-- .../docs/guide/example_projects/fair_roulette.md | 10 +++++----- documentation/docs/guide/solo/what-is-solo.md | 2 +- documentation/temp/misc/install.md | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/wasm/README.md b/contracts/wasm/README.md index 962066d3b0..b054cb71c2 100644 --- a/contracts/wasm/README.md +++ b/contracts/wasm/README.md @@ -72,14 +72,14 @@ Prerequisites: [downloaded here](https://rustwasm.github.io/wasm-pack/). Building a Rust smart contract is very simple when using the Rust plugin in any -IntelliJ based development environment. Open the _contracts/rust_ sub folder in +IntelliJ based development environment. Open the _contracts/wasm_ sub folder in your IntelliJ, which then provides you with the Rust workspace. The easiest way to create a new contract is to copy the _helloworld_ folder to a properly named new folder within the _rust_ sub folder. Next, change the fields in the first section of the new folder's _cargo.toml_ file to match your preferences. Make sure the package name equals the folder name. Finally, add the -new folder to the workspace in the _cargo.toml_ in the _contracts/rust_ folder. +new folder to the workspace in the _cargo.toml_ in the _contracts/wasm_ folder. To build the new smart contract select _Run->Edit Configurations_. Add a new configuration based on the _wasmpack_ template, type the _name_ of the new diff --git a/documentation/docs/guide/example_projects/fair_roulette.md b/documentation/docs/guide/example_projects/fair_roulette.md index a475b6e17f..4713eae723 100644 --- a/documentation/docs/guide/example_projects/fair_roulette.md +++ b/documentation/docs/guide/example_projects/fair_roulette.md @@ -65,7 +65,7 @@ Off-ledger requests are directly sent to Wasp nodes and do not require validatio :::note This example uses On-ledger requests to initiate a betting request. A method to invoke Off-ledger requests is implemented inside the frontend. -See: [placeBetOffLedger](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/rust/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts#L133) +See: [placeBetOffLedger](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts#L133) ::: #### Funds @@ -217,7 +217,7 @@ This service comprises two parts: ##### PlaceBetOnLedger -The [placeBetOnLedger](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/rust/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts#L149) function is responsible for sending On-Ledger bet requests. It constructs a simple OnLedger object containing: +The [placeBetOnLedger](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts#L149) function is responsible for sending On-Ledger bet requests. It constructs a simple OnLedger object containing: * The smart contract ID: `fairroulette` * The function to invoke: `placeBet` @@ -235,7 +235,7 @@ See: [CoreTypes](https://wiki.iota.org/wasp/misc/coretypes) and [Invoking](https ##### CallView -The [callView](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/rust/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts#L165) function is responsible for calling smart contract view functions. +The [callView](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts#L165) function is responsible for calling smart contract view functions. See: [Calling a view](https://wiki.iota.org/wasp/guide/solo/view-sc) @@ -246,9 +246,9 @@ State changes that happen afterwards are published through the websocket event s You can find examples to guide you in building similar functions in: -* Frontend: [getRoundStatus](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/rust/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts#L181) +* Frontend: [getRoundStatus](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/wasm/fairroulette/frontend/src/lib/fairroulette_client/fair_roulette_service.ts#L181) -* Smart Contract: [view_round_status](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/rust/fairroulette/src/fairroulette.rs#L312) +* Smart Contract: [view_round_status](https://github.com/iotaledger/wasp/blob/7b3ddc54891ccf021c7aaa32db35d88361fade16/contracts/wasm/fairroulette/src/fairroulette.rs#L312) Since data returned by the views is encoded in Base64, the frontend needs to decode this by using simple `Buffer` methods. The `view_round_status` view returns an `UInt16` which has a state of either `0` or `1`. diff --git a/documentation/docs/guide/solo/what-is-solo.md b/documentation/docs/guide/solo/what-is-solo.md index e3661fe9a9..28fbe2c076 100644 --- a/documentation/docs/guide/solo/what-is-solo.md +++ b/documentation/docs/guide/solo/what-is-solo.md @@ -45,7 +45,7 @@ The GoLang environment with the Rust plugin is a good combination. You can find example implementations of smart contracts (including source code and tests) in the Wasp repository, in the -[contracts/rust folder](https://github.com/iotaledger/wasp/tree/master/contracts/rust). +[contracts/wasm folder](https://github.com/iotaledger/wasp/tree/master/contracts/wasm). :::tip You can find the documentation for all the functionalities available in solo in [go-docs](https://pkg.go.dev/github.com/iotaledger/wasp/packages/solo). diff --git a/documentation/temp/misc/install.md b/documentation/temp/misc/install.md index 33b8045c20..93a8c94370 100644 --- a/documentation/temp/misc/install.md +++ b/documentation/temp/misc/install.md @@ -26,4 +26,4 @@ environment with the Rust plugin is a good combination. You can find example implementations of smart contracts (including source code and tests) in the Wasp repository, in the -[contracts/rust folder](https://github.com/iotaledger/wasp/tree/master/contracts/rust). +[contracts/wasm folder](https://github.com/iotaledger/wasp/tree/master/contracts/wasm). From b77612da32d6d5f04d2de1c35ab3b884855674d2 Mon Sep 17 00:00:00 2001 From: HowJMay Date: Wed, 2 Feb 2022 20:45:21 +0800 Subject: [PATCH 118/120] chore: Remove empty lines in interface New linter doesn't allow empty lines in interface, and those empty lines are removed in this PR. --- packages/chain/chain.go | 9 --------- packages/kv/kv.go | 2 -- packages/metrics/nodeconnmetrics/interface.go | 3 --- packages/peering/peering.go | 1 - 4 files changed, 15 deletions(-) diff --git a/packages/chain/chain.go b/packages/chain/chain.go index e4909de394..bf3ba00867 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -50,7 +50,6 @@ type ChainCore interface { type ChainEntry interface { ReceiveTransaction(*ledgerstate.Transaction) ReceiveState(stateOutput *ledgerstate.AliasOutput, timestamp time.Time) - Dismiss(reason string) IsDismissed() bool } @@ -100,19 +99,15 @@ type ( type NodeConnection interface { Subscribe(addr ledgerstate.Address) Unsubscribe(addr ledgerstate.Address) - AttachToTransactionReceived(*ledgerstate.AliasAddress, NodeConnectionHandleTransactionFun) AttachToInclusionStateReceived(*ledgerstate.AliasAddress, NodeConnectionHandleInclusionStateFun) AttachToOutputReceived(*ledgerstate.AliasAddress, NodeConnectionHandleOutputFun) AttachToUnspentAliasOutputReceived(*ledgerstate.AliasAddress, NodeConnectionHandleUnspentAliasOutputFun) - PullState(addr *ledgerstate.AliasAddress) PullTransactionInclusionState(addr ledgerstate.Address, txid ledgerstate.TransactionID) PullConfirmedOutput(addr ledgerstate.Address, outputID ledgerstate.OutputID) PostTransaction(tx *ledgerstate.Transaction) - GetMetrics() nodeconnmetrics.NodeConnectionMetrics - DetachFromTransactionReceived(*ledgerstate.AliasAddress) DetachFromInclusionStateReceived(*ledgerstate.AliasAddress) DetachFromOutputReceived(*ledgerstate.AliasAddress) @@ -125,14 +120,11 @@ type ChainNodeConnection interface { AttachToInclusionStateReceived(NodeConnectionHandleInclusionStateFun) AttachToOutputReceived(NodeConnectionHandleOutputFun) AttachToUnspentAliasOutputReceived(NodeConnectionHandleUnspentAliasOutputFun) - PullState() PullTransactionInclusionState(txid ledgerstate.TransactionID) PullConfirmedOutput(outputID ledgerstate.OutputID) PostTransaction(tx *ledgerstate.Transaction) - GetMetrics() nodeconnmetrics.NodeConnectionMessagesMetrics - DetachFromTransactionReceived() DetachFromInclusionStateReceived() DetachFromOutputReceived() @@ -223,7 +215,6 @@ type ConsensusWorkflowStatus interface { IsTransactionPosted() bool IsTransactionSeen() bool IsInProgress() bool - GetBatchProposalSentTime() time.Time GetConsensusBatchKnownTime() time.Time GetVMStartedTime() time.Time diff --git a/packages/kv/kv.go b/packages/kv/kv.go index 935e3dbbc8..2c11987fff 100644 --- a/packages/kv/kv.go +++ b/packages/kv/kv.go @@ -43,7 +43,6 @@ type KVWriter interface { type KVIterator interface { Iterate(prefix Key, f func(key Key, value []byte) bool) error IterateKeys(prefix Key, f func(key Key) bool) error - IterateSorted(prefix Key, f func(key Key, value []byte) bool) error IterateKeysSorted(prefix Key, f func(key Key) bool) error } @@ -57,7 +56,6 @@ type KVMustReader interface { type KVMustIterator interface { MustIterate(prefix Key, f func(key Key, value []byte) bool) MustIterateKeys(prefix Key, f func(key Key) bool) - MustIterateSorted(prefix Key, f func(key Key, value []byte) bool) MustIterateKeysSorted(prefix Key, f func(key Key) bool) } diff --git a/packages/metrics/nodeconnmetrics/interface.go b/packages/metrics/nodeconnmetrics/interface.go index bf073bcbbe..b4b6ea4dc5 100644 --- a/packages/metrics/nodeconnmetrics/interface.go +++ b/packages/metrics/nodeconnmetrics/interface.go @@ -9,7 +9,6 @@ import ( type NodeConnectionMessageMetrics interface { CountLastMessage(interface{}) - GetMessageTotal() uint32 GetLastEvent() time.Time GetLastMessage() interface{} @@ -20,7 +19,6 @@ type NodeConnectionMessagesMetrics interface { GetOutPullTransactionInclusionState() NodeConnectionMessageMetrics GetOutPullConfirmedOutput() NodeConnectionMessageMetrics GetOutPostTransaction() NodeConnectionMessageMetrics - GetInTransaction() NodeConnectionMessageMetrics GetInInclusionState() NodeConnectionMessageMetrics GetInOutput() NodeConnectionMessageMetrics @@ -33,7 +31,6 @@ type NodeConnectionMetrics interface { SetSubscribed(ledgerstate.Address) SetUnsubscribed(ledgerstate.Address) GetSubscribed() []ledgerstate.Address - RegisterMetrics() NewMessagesMetrics(chainID *iscp.ChainID) NodeConnectionMessagesMetrics } diff --git a/packages/peering/peering.go b/packages/peering/peering.go index 2bb7af01d6..a9af246727 100644 --- a/packages/peering/peering.go +++ b/packages/peering/peering.go @@ -101,7 +101,6 @@ type PeerDomainProvider interface { // PeerSender represents an interface to some remote peer. type PeerSender interface { - // NetID identifies the peer. NetID() string From f1ffe01d8910ca02285d82124fa080b4faee80cc Mon Sep 17 00:00:00 2001 From: Kumar Anirudha Date: Wed, 2 Feb 2022 18:51:29 +0530 Subject: [PATCH 119/120] fixed typo --- documentation/docs/guide/core_concepts/consensus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/guide/core_concepts/consensus.md b/documentation/docs/guide/core_concepts/consensus.md index 9caeab9032..d173018a48 100644 --- a/documentation/docs/guide/core_concepts/consensus.md +++ b/documentation/docs/guide/core_concepts/consensus.md @@ -35,7 +35,7 @@ The anchor transaction contains chain state transition, the AliasOutput and toke **It is only possible to produce valid signatures of inputs of the anchor transaction by the quorum** of nodes. In this case, a confirmed anchor transaction becomes a cryptographical **proof of consensus** in the committee. -To archive this, IOTA Smart Contracts uses **BLS threshold signatures in combination with polynomial (Shamir) secret sharing** to identify the address controlling the chain state. In order for the secret keys to be distributed across the chain validators, a DKG (Distributed Key Generation) procedure is executed when starting a chain (using the Rabin-Gennaro algorithm). +To achieve this, IOTA Smart Contracts uses **BLS threshold signatures in combination with polynomial (Shamir) secret sharing** to identify the address controlling the chain state. In order for the secret keys to be distributed across the chain validators, a DKG (Distributed Key Generation) procedure is executed when starting a chain (using the Rabin-Gennaro algorithm). ## The Consensus Algorithm From 7686d628b8f23a8094e04dde654c04198123554f Mon Sep 17 00:00:00 2001 From: Ivange Larry Date: Wed, 2 Feb 2022 15:22:55 +0100 Subject: [PATCH 120/120] Implement write ahead log for state manager (#687) * Implement write ahead log for state manager --- main.go | 2 + packages/chain/chain.go | 15 +- packages/chain/chainimpl/chainimpl.go | 6 +- packages/chain/chainimpl/eventproc.go | 2 +- packages/chain/consensus/action.go | 11 + packages/chain/consensus/consensus.go | 14 +- packages/chain/consensus/setup_test.go | 3 +- packages/chain/statemgr/setup_test.go | 3 +- packages/chain/statemgr/statemgr.go | 3 + packages/chain/statemgr/syncing_block.go | 16 ++ packages/chain/statemgr/syncmgr.go | 37 ++- packages/chains/chains.go | 13 +- packages/kv/kv.go | 2 - packages/metrics/nodeconnmetrics/interface.go | 4 - packages/parameters/parameters.go | 6 + packages/peering/peering.go | 4 +- packages/wal/wal.go | 214 ++++++++++++++++++ packages/webapi/admapi/activatechain.go | 7 +- packages/webapi/admapi/endpoints.go | 4 +- packages/webapi/endpoints.go | 3 + plugins/chains/plugin.go | 3 +- plugins/wal/plugin.go | 35 +++ plugins/webapi/plugin.go | 2 + tools/cluster/templates/waspconfig.go | 4 + tools/cluster/tests/wal_test.go | 76 +++++++ 25 files changed, 458 insertions(+), 31 deletions(-) create mode 100644 packages/wal/wal.go create mode 100644 plugins/wal/plugin.go create mode 100644 tools/cluster/tests/wal_test.go diff --git a/main.go b/main.go index 1944d8dbf9..0611734d9b 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( "github.com/iotaledger/wasp/plugins/profiling" "github.com/iotaledger/wasp/plugins/publishernano" "github.com/iotaledger/wasp/plugins/registry" + "github.com/iotaledger/wasp/plugins/wal" "github.com/iotaledger/wasp/plugins/wasmtimevm" "github.com/iotaledger/wasp/plugins/webapi" ) @@ -43,6 +44,7 @@ func main() { nodeconn.Init(), processors.Init(), wasmtimevm.Init(), + wal.Init(), chains.Init(), metrics.Init(), webapi.Init(), diff --git a/packages/chain/chain.go b/packages/chain/chain.go index e4909de394..eb5dba9e7b 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -50,7 +50,6 @@ type ChainCore interface { type ChainEntry interface { ReceiveTransaction(*ledgerstate.Transaction) ReceiveState(stateOutput *ledgerstate.AliasOutput, timestamp time.Time) - Dismiss(reason string) IsDismissed() bool } @@ -100,19 +99,15 @@ type ( type NodeConnection interface { Subscribe(addr ledgerstate.Address) Unsubscribe(addr ledgerstate.Address) - AttachToTransactionReceived(*ledgerstate.AliasAddress, NodeConnectionHandleTransactionFun) AttachToInclusionStateReceived(*ledgerstate.AliasAddress, NodeConnectionHandleInclusionStateFun) AttachToOutputReceived(*ledgerstate.AliasAddress, NodeConnectionHandleOutputFun) AttachToUnspentAliasOutputReceived(*ledgerstate.AliasAddress, NodeConnectionHandleUnspentAliasOutputFun) - PullState(addr *ledgerstate.AliasAddress) PullTransactionInclusionState(addr ledgerstate.Address, txid ledgerstate.TransactionID) PullConfirmedOutput(addr ledgerstate.Address, outputID ledgerstate.OutputID) PostTransaction(tx *ledgerstate.Transaction) - GetMetrics() nodeconnmetrics.NodeConnectionMetrics - DetachFromTransactionReceived(*ledgerstate.AliasAddress) DetachFromInclusionStateReceived(*ledgerstate.AliasAddress) DetachFromOutputReceived(*ledgerstate.AliasAddress) @@ -125,14 +120,11 @@ type ChainNodeConnection interface { AttachToInclusionStateReceived(NodeConnectionHandleInclusionStateFun) AttachToOutputReceived(NodeConnectionHandleOutputFun) AttachToUnspentAliasOutputReceived(NodeConnectionHandleUnspentAliasOutputFun) - PullState() PullTransactionInclusionState(txid ledgerstate.TransactionID) PullConfirmedOutput(outputID ledgerstate.OutputID) PostTransaction(tx *ledgerstate.Transaction) - GetMetrics() nodeconnmetrics.NodeConnectionMessagesMetrics - DetachFromTransactionReceived() DetachFromInclusionStateReceived() DetachFromOutputReceived() @@ -187,6 +179,12 @@ type AsynchronousCommonSubsetRunner interface { Close() } +type WAL interface { + Write(bytes []byte) error + Contains(i uint32) bool + Read(i uint32) ([]byte, error) +} + type MempoolInfo struct { TotalPool int ReadyCounter int @@ -223,7 +221,6 @@ type ConsensusWorkflowStatus interface { IsTransactionPosted() bool IsTransactionSeen() bool IsInProgress() bool - GetBatchProposalSentTime() time.Time GetConsensusBatchKnownTime() time.Time GetVMStartedTime() time.Time diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index dd1657e388..639613b32e 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -82,6 +82,7 @@ type chainObj struct { missingRequestIDsPeerMsgPipe pipe.Pipe missingRequestPeerMsgPipe pipe.Pipe timerTickMsgPipe pipe.Pipe + wal chain.WAL } type committeeStruct struct { @@ -102,6 +103,7 @@ func NewChain( offledgerBroadcastInterval time.Duration, pullMissingRequestsFromCommittee bool, chainMetrics metrics.ChainMetrics, + wal chain.WAL, ) chain.Chain { log.Debugf("creating chain object for %s", chainID.String()) @@ -138,6 +140,7 @@ func NewChain( missingRequestIDsPeerMsgPipe: pipe.NewLimitInfinitePipe(maxMsgBuffer), missingRequestPeerMsgPipe: pipe.NewLimitInfinitePipe(maxMsgBuffer), timerTickMsgPipe: pipe.NewLimitInfinitePipe(1), + wal: wal, } ret.committee.Store(&committeeStruct{}) @@ -153,7 +156,8 @@ func NewChain( log.Errorf("NewChain: unable to create stateMgr.fallbackPeers domain: %v", err) return nil } - ret.stateMgr = statemgr.New(db, ret, stateMgrDomain, ret.nodeConn, chainMetrics) + + ret.stateMgr = statemgr.New(db, ret, stateMgrDomain, ret.nodeConn, chainMetrics, wal) ret.stateMgr.SetChainPeers(chainPeerNodes) ret.eventChainTransitionClosure = events.NewClosure(ret.processChainTransition) diff --git a/packages/chain/chainimpl/eventproc.go b/packages/chain/chainimpl/eventproc.go index 5a351f7163..fd83d22a06 100644 --- a/packages/chain/chainimpl/eventproc.go +++ b/packages/chain/chainimpl/eventproc.go @@ -219,7 +219,7 @@ func (c *chainObj) createNewCommitteeAndConsensus(dkShare *tcrypto.DKShare) erro cmtPeerGroup.Detach(attachID) } c.log.Debugf("creating new consensus object...") - c.consensus = consensus.New(c, c.mempool, cmt, cmtPeerGroup, c.nodeConn, c.pullMissingRequestsFromCommittee, c.chainMetrics) + c.consensus = consensus.New(c, c.mempool, cmt, cmtPeerGroup, c.nodeConn, c.pullMissingRequestsFromCommittee, c.chainMetrics, c.wal) c.setCommittee(cmt) c.log.Infof("NEW COMMITTEE OF VALIDATORS has been initialized for the state address %s", dkShare.Address.Base58()) diff --git a/packages/chain/consensus/action.go b/packages/chain/consensus/action.go index ee9b21cdea..0bba2ca6f3 100644 --- a/packages/chain/consensus/action.go +++ b/packages/chain/consensus/action.go @@ -344,6 +344,7 @@ func (c *consensus) checkQuorum() { if !chainOutput.GetIsGovernanceUpdated() { // if it is not state controller rotation, sending message to state manager // Otherwise state manager is not notified + c.writeToWAL() chainOutputID := chainOutput.ID() c.chain.StateCandidateToStateManager(c.resultState, chainOutputID) c.log.Debugf("checkQuorum: StateCandidateMsg sent for state index %v, approving output ID %v", @@ -367,6 +368,16 @@ func (c *consensus) checkQuorum() { c.pullInclusionStateDeadline = time.Now() } +func (c *consensus) writeToWAL() { + block, err := c.resultState.ExtractBlock() + if err == nil { + err = c.wal.Write(block.Bytes()) + if err != nil { + c.log.Debugf("Error writing block to wal: %v", err) + } + } +} + // postTransactionIfNeeded posts a finalized transaction upon deadline unless it was evidenced on L1 before the deadline. func (c *consensus) postTransactionIfNeeded() { if !c.workflow.IsTransactionFinalized() { diff --git a/packages/chain/consensus/consensus.go b/packages/chain/consensus/consensus.go index 789252a96c..2f13161618 100644 --- a/packages/chain/consensus/consensus.go +++ b/packages/chain/consensus/consensus.go @@ -68,6 +68,7 @@ type consensus struct { pullMissingRequestsFromCommittee bool receivePeerMessagesAttachID interface{} consensusMetrics metrics.ConsensusMetrics + wal chain.WAL } var _ chain.Consensus = &consensus{} @@ -79,7 +80,17 @@ const ( maxMsgBuffer = 1000 ) -func New(chainCore chain.ChainCore, mempool chain.Mempool, committee chain.Committee, peerGroup peering.GroupProvider, nodeConn chain.ChainNodeConnection, pullMissingRequestsFromCommittee bool, consensusMetrics metrics.ConsensusMetrics, timersOpt ...ConsensusTimers) chain.Consensus { +func New( + chainCore chain.ChainCore, + mempool chain.Mempool, + committee chain.Committee, + peerGroup peering.GroupProvider, + nodeConn chain.ChainNodeConnection, + pullMissingRequestsFromCommittee bool, + consensusMetrics metrics.ConsensusMetrics, + wal chain.WAL, + timersOpt ...ConsensusTimers, +) chain.Consensus { var timers ConsensusTimers if len(timersOpt) > 0 { timers = timersOpt[0] @@ -109,6 +120,7 @@ func New(chainCore chain.ChainCore, mempool chain.Mempool, committee chain.Commi assert: assert.NewAssert(log), pullMissingRequestsFromCommittee: pullMissingRequestsFromCommittee, consensusMetrics: consensusMetrics, + wal: wal, } ret.receivePeerMessagesAttachID = ret.committeePeerGroup.Attach(peering.PeerMessageReceiverConsensus, ret.receiveCommitteePeerMessages) ret.nodeConn.AttachToInclusionStateReceived(func(txID ledgerstate.TransactionID, inclusionState ledgerstate.InclusionState) { diff --git a/packages/chain/consensus/setup_test.go b/packages/chain/consensus/setup_test.go index 4c38af0492..e1dd02dfd4 100644 --- a/packages/chain/consensus/setup_test.go +++ b/packages/chain/consensus/setup_test.go @@ -40,6 +40,7 @@ import ( "github.com/iotaledger/wasp/packages/testutil/testpeers" "github.com/iotaledger/wasp/packages/transaction" "github.com/iotaledger/wasp/packages/util" + "github.com/iotaledger/wasp/packages/wal" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" ) @@ -252,7 +253,7 @@ func (env *MockedEnv) NewNode(nodeIndex uint16, timers ConsensusTimers) *mockedN ret.stateSync.SetSolidIndex(0) require.NoError(env.T, err) - cons := New(ret.ChainCore, ret.Mempool, cmt, cmtPeerGroup, ret.NodeConn, true, metrics.DefaultChainMetrics(), timers) + cons := New(ret.ChainCore, ret.Mempool, cmt, cmtPeerGroup, ret.NodeConn, true, metrics.DefaultChainMetrics(), wal.NewDefault(), timers) cons.(*consensus).vmRunner = testchain.NewMockedVMRunner(env.T, log) ret.Consensus = cons diff --git a/packages/chain/statemgr/setup_test.go b/packages/chain/statemgr/setup_test.go index ef33b26ebf..5ee1257571 100644 --- a/packages/chain/statemgr/setup_test.go +++ b/packages/chain/statemgr/setup_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/iotaledger/wasp/packages/iscp/colored" + "github.com/iotaledger/wasp/packages/wal" "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/goshimmer/packages/ledgerstate/utxodb" @@ -238,7 +239,7 @@ func (env *MockedEnv) NewMockedNode(nodeIndex int, timers StateManagerTimers) *M }) } }) - ret.StateManager = New(ret.store, ret.ChainCore, stateMgrDomain, ret.NodeConn, stateMgrMetrics, timers) + ret.StateManager = New(ret.store, ret.ChainCore, stateMgrDomain, ret.NodeConn, stateMgrMetrics, wal.NewDefault(), timers) ret.StateTransition = testchain.NewMockedStateTransition(env.T, env.OriginatorKeyPair) ret.StateTransition.OnNextState(func(vstate state.VirtualStateAccess, tx *ledgerstate.Transaction) { log.Debugf("MockedEnv.onNextState: state index %d", vstate.BlockIndex()) diff --git a/packages/chain/statemgr/statemgr.go b/packages/chain/statemgr/statemgr.go index 481915f612..9af59c0d91 100644 --- a/packages/chain/statemgr/statemgr.go +++ b/packages/chain/statemgr/statemgr.go @@ -48,6 +48,7 @@ type stateManager struct { eventStateCandidateMsgPipe pipe.Pipe eventTimerMsgPipe pipe.Pipe stateManagerMetrics metrics.StateManagerMetrics + wal chain.WAL } var _ chain.StateManager = &stateManager{} @@ -67,6 +68,7 @@ func New( domain *DomainWithFallback, nodeconn chain.ChainNodeConnection, stateManagerMetrics metrics.StateManagerMetrics, + wal chain.WAL, timersOpt ...StateManagerTimers, ) chain.StateManager { var timers StateManagerTimers @@ -92,6 +94,7 @@ func New( eventStateCandidateMsgPipe: pipe.NewLimitInfinitePipe(maxMsgBuffer), eventTimerMsgPipe: pipe.NewLimitInfinitePipe(1), stateManagerMetrics: stateManagerMetrics, + wal: wal, } ret.receivePeerMessagesAttachID = ret.domain.Attach(peering.PeerMessageReceiverStateManager, ret.receiveChainPeerMessages) ret.nodeConn.AttachToOutputReceived(ret.EnqueueOutputMsg) diff --git a/packages/chain/statemgr/syncing_block.go b/packages/chain/statemgr/syncing_block.go index 08db76b5c9..85ea617733 100644 --- a/packages/chain/statemgr/syncing_block.go +++ b/packages/chain/statemgr/syncing_block.go @@ -28,6 +28,7 @@ type syncingBlocks struct { type syncingBlock struct { requestBlockRetryTime time.Time blockCandidates map[hashing.HashValue]*candidateBlock + receivedFromWAL bool } func newSyncingBlocks(log *logger.Logger, initialBlockRetry time.Duration) *syncingBlocks { @@ -208,3 +209,18 @@ func (syncsT *syncingBlocks) blockPollFallbackNeeded() bool { } return syncsT.lastPullTime.Sub(syncsT.lastRecvTime) >= pollFallbackDelay } + +func (syncsT *syncingBlocks) setReceivedFromWAL(i uint32) { + block, ok := syncsT.blocks[i] + if ok { + block.receivedFromWAL = true + } +} + +func (syncsT *syncingBlocks) isObtainedFromWAL(i uint32) bool { + block, ok := syncsT.blocks[i] + if ok { + return block.receivedFromWAL + } + return false +} diff --git a/packages/chain/statemgr/syncmgr.go b/packages/chain/statemgr/syncmgr.go index ec45241a2c..795e403841 100644 --- a/packages/chain/statemgr/syncmgr.go +++ b/packages/chain/statemgr/syncmgr.go @@ -81,6 +81,12 @@ func (sm *stateManager) doSyncActionIfNeeded() { for i := startSyncFromIndex; i <= sm.stateOutput.GetStateIndex(); i++ { requestBlockRetryTime := sm.syncingBlocks.getRequestBlockRetryTime(i) blockCandidatesCount := sm.syncingBlocks.getBlockCandidatesCount(i) + if blockCandidatesCount == 0 { + if sm.candidateBlockInWAL(i) { + blockCandidatesCount++ + sm.syncingBlocks.setReceivedFromWAL(i) + } + } approvedBlockCandidatesCount := sm.syncingBlocks.getApprovedBlockCandidatesCount(i) sm.log.Debugf("doSyncAction: trying to sync state for index %v; requestBlockRetryTime %v, blockCandidates count %v, approved blockCandidates count %v", i, requestBlockRetryTime, blockCandidatesCount, approvedBlockCandidatesCount) @@ -90,7 +96,7 @@ func (sm *stateManager) doSyncActionIfNeeded() { return } nowis := time.Now() - if nowis.After(requestBlockRetryTime) { + if !sm.syncingBlocks.isObtainedFromWAL(i) && nowis.After(requestBlockRetryTime) { // have to pull sm.log.Debugf("doSyncAction: requesting block index %v, fallback=%v from %v random peers.", i, sm.domain.GetFallbackMode(), numberOfNodesToRequestBlockFromConst) getBlockMsg := &messages.GetBlockMsg{BlockIndex: i} @@ -118,6 +124,35 @@ func (sm *stateManager) doSyncActionIfNeeded() { } } +func (sm *stateManager) candidateBlockInWAL(i uint32) bool { + if !sm.wal.Contains(i) { + sm.log.Debugf("candidateBlockInWAL: block with index %d not found in wal.", i) + return false + } + blockBytes, err := sm.wal.Read(i) + if err != nil { + sm.log.Debugf("candidateBlockInWAL: error reading block bytes for %d. %v", i, err) + return false + } + block, err := state.BlockFromBytes(blockBytes) + if err != nil { + sm.log.Debugf("candidateBlockInWAL: error reading block bytes for %d. %v", i, err) + return false + } + nextState := sm.solidState.Copy() + err = nextState.ApplyBlock(block) + if err != nil { + sm.log.Debugf("candidateBlockInWAL: error applying block %d. %v", i, err) + return false + } + _, candidate := sm.syncingBlocks.addBlockCandidate(block, nextState) + if candidate == nil { + return false + } + candidate.approveIfRightOutput(sm.stateOutput) + return true +} + func (sm *stateManager) getCandidatesToCommit(candidateAcc []*candidateBlock, calculatedPrevState state.VirtualStateAccess, fromStateIndex, toStateIndex uint32) ([]*candidateBlock, state.VirtualStateAccess, bool) { sm.log.Debugf("getCandidatesToCommit from %v to %v", fromStateIndex, toStateIndex) if fromStateIndex > toStateIndex { diff --git a/packages/chains/chains.go b/packages/chains/chains.go index d15fc7e716..f55a84a65e 100644 --- a/packages/chains/chains.go +++ b/packages/chains/chains.go @@ -18,6 +18,7 @@ import ( "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/vm/processors" + "github.com/iotaledger/wasp/packages/wal" "golang.org/x/xerrors" ) @@ -83,7 +84,7 @@ func (c *Chains) SetNodeConn(nodeConn chain.NodeConnection) { c.nodeConn = nodeConn } -func (c *Chains) ActivateAllFromRegistry(registryProvider registry.Provider, allMetrics *metrics.Metrics) error { +func (c *Chains) ActivateAllFromRegistry(registryProvider registry.Provider, allMetrics *metrics.Metrics, w *wal.WAL) error { chainRecords, err := registryProvider().GetChainRecords() if err != nil { return err @@ -97,7 +98,7 @@ func (c *Chains) ActivateAllFromRegistry(registryProvider registry.Provider, all for _, chr := range chainRecords { if chr.Active { - if err := c.Activate(chr, registryProvider, allMetrics); err != nil { + if err := c.Activate(chr, registryProvider, allMetrics, w); err != nil { c.log.Errorf("cannot activate chain %s: %v", chr.ChainID, err) } } @@ -109,7 +110,7 @@ func (c *Chains) ActivateAllFromRegistry(registryProvider registry.Provider, all // - creates chain object // - insert it into the runtime registry // - subscribes for related transactions in he IOTA node -func (c *Chains) Activate(chr *registry.ChainRecord, registryProvider registry.Provider, allMetrics *metrics.Metrics) error { +func (c *Chains) Activate(chr *registry.ChainRecord, registryProvider registry.Provider, allMetrics *metrics.Metrics, w *wal.WAL) error { c.mutex.Lock() defer c.mutex.Unlock() @@ -126,6 +127,11 @@ func (c *Chains) Activate(chr *registry.ChainRecord, registryProvider registry.P defaultRegistry := registryProvider() chainKVStore := c.getOrCreateKVStore(chr.ChainID) chainMetrics := allMetrics.NewChainMetrics(chr.ChainID) + chainWAL, err := w.NewChainWAL(chr.ChainID) + if err != nil { + c.log.Debugf("Error creating wal object: %v", err) + chainWAL = wal.NewDefault() + } newChain := chainimpl.NewChain( chr.ChainID, c.log, @@ -139,6 +145,7 @@ func (c *Chains) Activate(chr *registry.ChainRecord, registryProvider registry.P c.offledgerBroadcastInterval, c.pullMissingRequestsFromCommittee, chainMetrics, + chainWAL, ) if newChain == nil { return xerrors.New("Chains.Activate: failed to create chain object") diff --git a/packages/kv/kv.go b/packages/kv/kv.go index 935e3dbbc8..2c11987fff 100644 --- a/packages/kv/kv.go +++ b/packages/kv/kv.go @@ -43,7 +43,6 @@ type KVWriter interface { type KVIterator interface { Iterate(prefix Key, f func(key Key, value []byte) bool) error IterateKeys(prefix Key, f func(key Key) bool) error - IterateSorted(prefix Key, f func(key Key, value []byte) bool) error IterateKeysSorted(prefix Key, f func(key Key) bool) error } @@ -57,7 +56,6 @@ type KVMustReader interface { type KVMustIterator interface { MustIterate(prefix Key, f func(key Key, value []byte) bool) MustIterateKeys(prefix Key, f func(key Key) bool) - MustIterateSorted(prefix Key, f func(key Key, value []byte) bool) MustIterateKeysSorted(prefix Key, f func(key Key) bool) } diff --git a/packages/metrics/nodeconnmetrics/interface.go b/packages/metrics/nodeconnmetrics/interface.go index bf073bcbbe..53021cbf06 100644 --- a/packages/metrics/nodeconnmetrics/interface.go +++ b/packages/metrics/nodeconnmetrics/interface.go @@ -9,7 +9,6 @@ import ( type NodeConnectionMessageMetrics interface { CountLastMessage(interface{}) - GetMessageTotal() uint32 GetLastEvent() time.Time GetLastMessage() interface{} @@ -20,7 +19,6 @@ type NodeConnectionMessagesMetrics interface { GetOutPullTransactionInclusionState() NodeConnectionMessageMetrics GetOutPullConfirmedOutput() NodeConnectionMessageMetrics GetOutPostTransaction() NodeConnectionMessageMetrics - GetInTransaction() NodeConnectionMessageMetrics GetInInclusionState() NodeConnectionMessageMetrics GetInOutput() NodeConnectionMessageMetrics @@ -29,11 +27,9 @@ type NodeConnectionMessagesMetrics interface { type NodeConnectionMetrics interface { NodeConnectionMessagesMetrics - SetSubscribed(ledgerstate.Address) SetUnsubscribed(ledgerstate.Address) GetSubscribed() []ledgerstate.Address - RegisterMetrics() NewMessagesMetrics(chainID *iscp.ChainID) NodeConnectionMessagesMetrics } diff --git a/packages/parameters/parameters.go b/packages/parameters/parameters.go index 3af670849b..a97a6461da 100644 --- a/packages/parameters/parameters.go +++ b/packages/parameters/parameters.go @@ -56,6 +56,9 @@ const ( MetricsBindAddress = "metrics.bindAddress" MetricsEnabled = "metrics.enabled" + + WALEnabled = "wal.enabled" + WALDirectory = "wal.directory" ) func Init() *configuration.Configuration { @@ -105,6 +108,9 @@ func Init() *configuration.Configuration { flag.String(MetricsBindAddress, "127.0.0.1:2112", "prometheus metrics http server address") flag.Bool(MetricsEnabled, false, "disable and enable prometheus metrics") + flag.Bool(WALEnabled, true, "enabled wal") + flag.String(WALDirectory, "wal", "path to logs folder") + return all } diff --git a/packages/peering/peering.go b/packages/peering/peering.go index 2bb7af01d6..0ca2003356 100644 --- a/packages/peering/peering.go +++ b/packages/peering/peering.go @@ -23,8 +23,7 @@ const ( // FirstUserMsgCode is the first committee message type. // All the equal and larger msg types are committee messages. // those with smaller are reserved by the package for heartbeat and handshake messages - FirstUserMsgCode = byte(0x10) - + FirstUserMsgCode = byte(0x10) PeerMessageReceiverStateManager = byte(iota) PeerMessageReceiverConsensus PeerMessageReceiverCommonSubset @@ -101,7 +100,6 @@ type PeerDomainProvider interface { // PeerSender represents an interface to some remote peer. type PeerSender interface { - // NetID identifies the peer. NetID() string diff --git a/packages/wal/wal.go b/packages/wal/wal.go new file mode 100644 index 0000000000..5267ea646e --- /dev/null +++ b/packages/wal/wal.go @@ -0,0 +1,214 @@ +package wal + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "sync" + + "github.com/iotaledger/hive.go/logger" + "github.com/iotaledger/wasp/packages/chain" + "github.com/iotaledger/wasp/packages/iscp" + "github.com/iotaledger/wasp/packages/state" + "github.com/prometheus/client_golang/prometheus" +) + +type WAL struct { + dir string + log *logger.Logger + metrics *walMetrics + segments map[uint32]*segment + synced map[uint32]bool + mu sync.RWMutex //nolint +} + +type chainWAL struct { + *WAL + chainID *iscp.ChainID +} + +func New(log *logger.Logger, dir string) *WAL { + return &WAL{log: log, dir: dir, metrics: newWALMetrics(), synced: make(map[uint32]bool)} +} + +var _ chain.WAL = &chainWAL{} + +type segmentFile interface { + Stat() (os.FileInfo, error) + io.Writer + io.Closer + io.Reader +} + +type segment struct { + segmentFile + index uint32 + dir string +} + +func (w *WAL) NewChainWAL(chainID *iscp.ChainID) (chain.WAL, error) { + if w == nil { + return &defaultWAL{}, nil + } + w.dir = filepath.Join(w.dir, chainID.Base58()) + if err := os.MkdirAll(w.dir, 0o777); err != nil { + return nil, fmt.Errorf("create dir: %w", err) + } + // read all segments in log + f, err := os.Open(w.dir) + if err != nil { + return nil, fmt.Errorf("could not open wal: %w", err) + } + + w.segments = make(map[uint32]*segment) + files, _ := f.ReadDir(-1) + for _, file := range files { + w.metrics.segments.Inc() + index, _ := strconv.ParseUint(file.Name(), 10, 32) + w.segments[uint32(index)] = &segment{index: uint32(index), dir: w.dir} + } + return &chainWAL{w, chainID}, nil +} + +func (w *chainWAL) Write(bytes []byte) error { + w.mu.Lock() + defer w.mu.Unlock() + + block, err := state.BlockFromBytes(bytes) + if err != nil { + return fmt.Errorf("Invalid block: %w", err) + } + segment, err := w.createSegment(block.BlockIndex()) + if err != nil { + w.metrics.failedWrites.Inc() + return fmt.Errorf("Error writing log: %w", err) + } + n, err := segment.Write(bytes) + if err != nil || len(bytes) != n { + w.metrics.failedReads.Inc() + return fmt.Errorf("Error writing log: %w", err) + } + w.metrics.segments.Inc() + return segment.Close() +} + +func (w *chainWAL) createSegment(i uint32) (*segment, error) { + segName := segmentName(w.dir, i) + f, err := os.OpenFile(segName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666) + if err != nil { + return nil, fmt.Errorf("could not create segment: %w", err) + } + s := &segment{index: i, segmentFile: f, dir: w.dir} + w.segments[i] = s + return s, nil +} + +func segmentName(dir string, index uint32) string { + return filepath.Join(dir, fmt.Sprintf("%010d", index)) +} + +func (w *chainWAL) Contains(i uint32) bool { + return w.getSegment(i) != nil +} + +func (w *chainWAL) Read(i uint32) ([]byte, error) { + segment := w.getSegment(i) + if segment == nil { + return nil, fmt.Errorf("block not found in wal") + } + if err := segment.load(); err != nil { + w.metrics.failedReads.Inc() + return nil, fmt.Errorf("Error opening backup file: %w", err) + } + stat, err := segment.Stat() + if err != nil { + w.metrics.failedReads.Inc() + return nil, fmt.Errorf("Error reading backup file: %w", err) + } + blockBytes := make([]byte, stat.Size()) + bufr := bufio.NewReader(segment) + n, err := bufr.Read(blockBytes) + if err != nil || int64(n) != stat.Size() { + w.metrics.failedReads.Inc() + return nil, fmt.Errorf("Error reading backup file: %w", err) + } + return blockBytes, nil +} + +func (w *chainWAL) getSegment(i uint32) *segment { + segment, ok := w.segments[i] + if ok { + return segment + } + return nil +} + +func (s *segment) load() error { + segName := segmentName(s.dir, s.index) + f, err := os.OpenFile(segName, os.O_RDONLY, 0o666) + if err != nil { + return fmt.Errorf("error opening segment: %w", err) + } + s.segmentFile = f + return nil +} + +type defaultWAL struct{} + +var _ chain.WAL = &defaultWAL{} + +func (w *defaultWAL) Write(_ []byte) error { + return nil +} + +func (w *defaultWAL) Read(i uint32) ([]byte, error) { + return nil, fmt.Errorf("Empty wal") +} + +func (w *defaultWAL) Contains(i uint32) bool { + return false +} + +func NewDefault() chain.WAL { + return &defaultWAL{} +} + +type walMetrics struct { + segments prometheus.Counter + failedWrites prometheus.Counter + failedReads prometheus.Counter +} + +var once sync.Once + +func newWALMetrics() *walMetrics { + m := &walMetrics{} + + m.segments = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "wasp_wal_total_segments", + Help: "Total number of segment files", + }) + + m.failedWrites = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "wasp_wal_failed_writes", + Help: "Total number of writes to WAL that failed", + }) + + m.failedReads = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "wasp_wal_failed_reads", + Help: "Total number of reads failed while replaying WAL", + }) + + registerMetrics := func() { + prometheus.MustRegister( + m.segments, + m.failedWrites, + m.failedReads, + ) + } + once.Do(registerMetrics) + return m +} diff --git a/packages/webapi/admapi/activatechain.go b/packages/webapi/admapi/activatechain.go index a34e663ceb..b9d26292ad 100644 --- a/packages/webapi/admapi/activatechain.go +++ b/packages/webapi/admapi/activatechain.go @@ -16,6 +16,7 @@ import ( "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/tcrypto" "github.com/iotaledger/wasp/packages/vm/core/governance" + "github.com/iotaledger/wasp/packages/wal" "github.com/iotaledger/wasp/packages/webapi/httperrors" "github.com/iotaledger/wasp/packages/webapi/model" "github.com/iotaledger/wasp/packages/webapi/routes" @@ -28,14 +29,16 @@ type chainWebAPI struct { chains chains.Provider network peering.NetworkProvider allMetrics *metrics.Metrics + w *wal.WAL } -func addChainEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Provider, chainsProvider chains.Provider, network peering.NetworkProvider, allMetrics *metrics.Metrics) { +func addChainEndpoints(adm echoswagger.ApiGroup, registryProvider registry.Provider, chainsProvider chains.Provider, network peering.NetworkProvider, allMetrics *metrics.Metrics, w *wal.WAL) { c := &chainWebAPI{ registryProvider, chainsProvider, network, allMetrics, + w, } adm.POST(routes.ActivateChain(":chainID"), c.handleActivateChain). @@ -66,7 +69,7 @@ func (w *chainWebAPI) handleActivateChain(c echo.Context) error { } log.Debugw("calling Chains.Activate", "chainID", rec.ChainID.String()) - if err := w.chains().Activate(rec, w.registry, w.allMetrics); err != nil { + if err := w.chains().Activate(rec, w.registry, w.allMetrics, w.w); err != nil { return err } diff --git a/packages/webapi/admapi/endpoints.go b/packages/webapi/admapi/endpoints.go index a164ec670c..2358e8bfe3 100644 --- a/packages/webapi/admapi/endpoints.go +++ b/packages/webapi/admapi/endpoints.go @@ -14,6 +14,7 @@ import ( "github.com/iotaledger/wasp/packages/parameters" "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/registry" + "github.com/iotaledger/wasp/packages/wal" "github.com/labstack/echo/v4" "github.com/pangpanglabs/echoswagger/v2" ) @@ -34,6 +35,7 @@ func AddEndpoints( nodeProvider dkg.NodeProvider, shutdown ShutdownFunc, metrics *metricspkg.Metrics, + w *wal.WAL, ) { initLogger() @@ -49,7 +51,7 @@ func AddEndpoints( addNodeOwnerEndpoints(adm, registryProvider) addChainRecordEndpoints(adm, registryProvider) addChainMetricsEndpoints(adm, chainsProvider) - addChainEndpoints(adm, registryProvider, chainsProvider, network, metrics) + addChainEndpoints(adm, registryProvider, chainsProvider, network, metrics, w) addDKSharesEndpoints(adm, registryProvider, nodeProvider) addPeeringEndpoints(adm, network, tnm) } diff --git a/packages/webapi/endpoints.go b/packages/webapi/endpoints.go index 6dc8e2faba..0c35c3e5e6 100644 --- a/packages/webapi/endpoints.go +++ b/packages/webapi/endpoints.go @@ -14,6 +14,7 @@ import ( "github.com/iotaledger/wasp/packages/parameters" "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/registry" + "github.com/iotaledger/wasp/packages/wal" "github.com/iotaledger/wasp/packages/webapi/admapi" "github.com/iotaledger/wasp/packages/webapi/info" "github.com/iotaledger/wasp/packages/webapi/reqstatus" @@ -36,6 +37,7 @@ func Init( nodeProvider dkg.NodeProvider, shutdown admapi.ShutdownFunc, metrics *metricspkg.Metrics, + w *wal.WAL, ) { log = logger.NewLogger("WebAPI") @@ -69,6 +71,7 @@ func Init( nodeProvider, shutdown, metrics, + w, ) log.Infof("added web api endpoints") } diff --git a/plugins/chains/plugin.go b/plugins/chains/plugin.go index 252423ff76..735a0df90d 100644 --- a/plugins/chains/plugin.go +++ b/plugins/chains/plugin.go @@ -18,6 +18,7 @@ import ( "github.com/iotaledger/wasp/plugins/peering" "github.com/iotaledger/wasp/plugins/processors" "github.com/iotaledger/wasp/plugins/registry" + "github.com/iotaledger/wasp/plugins/wal" ) const PluginName = "Chains" @@ -52,7 +53,7 @@ func run(_ *node.Plugin) { allMetrics = metrics.AllMetrics() } allChains.SetNodeConn(nodeconnimpl.NewNodeConnection(nodeconn.NodeConnection(), allMetrics.GetNodeConnectionMetrics(), log)) - if err := allChains.ActivateAllFromRegistry(registry.DefaultRegistry, allMetrics); err != nil { + if err := allChains.ActivateAllFromRegistry(registry.DefaultRegistry, allMetrics, wal.GetWAL()); err != nil { log.Errorf("failed to read chain activation records from registry: %v", err) return } diff --git a/plugins/wal/plugin.go b/plugins/wal/plugin.go new file mode 100644 index 0000000000..0666c754cc --- /dev/null +++ b/plugins/wal/plugin.go @@ -0,0 +1,35 @@ +package wal + +import ( + "github.com/iotaledger/hive.go/logger" + "github.com/iotaledger/hive.go/node" + "github.com/iotaledger/wasp/packages/parameters" + "github.com/iotaledger/wasp/packages/wal" +) + +const PluginName = "Wal" + +var ( + log *logger.Logger + w *wal.WAL +) + +func Init() *node.Plugin { + return node.NewPlugin(PluginName, node.Enabled, configure, run) +} + +func configure(_ *node.Plugin) { + if !parameters.GetBool(parameters.WALEnabled) { + return + } + log = logger.NewLogger(PluginName) + walDir := parameters.GetString(parameters.WALDirectory) + w = wal.New(log, walDir) +} + +func run(_ *node.Plugin) { +} + +func GetWAL() *wal.WAL { + return w +} diff --git a/plugins/webapi/plugin.go b/plugins/webapi/plugin.go index 3f29aea4aa..80d17a493a 100644 --- a/plugins/webapi/plugin.go +++ b/plugins/webapi/plugin.go @@ -22,6 +22,7 @@ import ( "github.com/iotaledger/wasp/plugins/metrics" "github.com/iotaledger/wasp/plugins/peering" "github.com/iotaledger/wasp/plugins/registry" + "github.com/iotaledger/wasp/plugins/wal" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/pangpanglabs/echoswagger/v2" @@ -86,6 +87,7 @@ func configure(*node.Plugin) { dkg.DefaultNode, gracefulshutdown.Shutdown, allMetrics, + wal.GetWAL(), ) } diff --git a/tools/cluster/templates/waspconfig.go b/tools/cluster/templates/waspconfig.go index ab0707202d..41e9467527 100644 --- a/tools/cluster/templates/waspconfig.go +++ b/tools/cluster/templates/waspconfig.go @@ -69,6 +69,10 @@ const WaspConfig = ` "metrics": { "bindAddress": "0.0.0.0:{{.MetricsPort}}", "enabled": false + }, + "wal": { + "directory": "wal", + "enabled": true } } ` diff --git a/tools/cluster/tests/wal_test.go b/tools/cluster/tests/wal_test.go new file mode 100644 index 0000000000..d75ceeebe2 --- /dev/null +++ b/tools/cluster/tests/wal_test.go @@ -0,0 +1,76 @@ +package tests + +import ( + "os" + "path" + "strconv" + "testing" + + "github.com/iotaledger/wasp/packages/kv/codec" + "github.com/iotaledger/wasp/packages/kv/dict" + "github.com/iotaledger/wasp/packages/state" + "github.com/iotaledger/wasp/packages/vm/core/blocklog" + "github.com/stretchr/testify/require" +) + +func TestWriteToWAL(t *testing.T) { + e := setupWithNoChain(t, 1) + + chain, err := e.clu.DeployDefaultChain() + require.NoError(t, err) + require.NoError(t, err) + + walDir := walDirFromDataPath(e.clu.DataPath, chain.ChainID.Base58()) + require.True(t, walDirectoryCreated(walDir)) + + blockIndex, _ := chain.BlockIndex(0) + checkCreatedFilenameMatchesBlockIndex(t, walDir, blockIndex) + + segName := latestSegName(walDir) + segPath := path.Join(walDir, segName) + blockBytes := getBytesFromSegment(t, segPath) + block, err := state.BlockFromBytes(blockBytes) + require.NoError(t, err) + require.EqualValues(t, blockIndex, block.BlockIndex()) + + v, err := chain.Cluster.WaspClient(0).CallView( + chain.ChainID, blocklog.Contract.Hname(), blocklog.FuncGetBlockInfo.Name, + dict.Dict{ + blocklog.ParamBlockIndex: codec.EncodeUint32(blockIndex), + }) + require.NoError(t, err) + + blockInfo, err := blocklog.BlockInfoFromBytes(blockIndex, v.MustGet(blocklog.ParamBlockInfo)) + require.NoError(t, err) + + require.EqualValues(t, blockInfo.BlockIndex, block.BlockIndex()) + require.EqualValues(t, blockInfo.Timestamp, block.Timestamp()) + require.EqualValues(t, blockInfo.PreviousStateHash.Bytes(), block.PreviousStateHash().Bytes()) +} + +func walDirectoryCreated(walDir string) bool { + _, err := os.Stat(walDir) + return !os.IsNotExist(err) +} + +func walDirFromDataPath(dataPath, chainID string) string { + return path.Join(dataPath, "wasp0", "wal", chainID) +} + +func checkCreatedFilenameMatchesBlockIndex(t *testing.T, walDir string, blockIndex uint32) { + latestSegmentName := latestSegName(walDir) + index, _ := strconv.ParseUint(latestSegmentName, 10, 32) + t.Logf("Index: %d", index) + require.EqualValues(t, blockIndex, index) +} + +func latestSegName(walDir string) string { + files, _ := os.ReadDir(walDir) + return files[len(files)-1].Name() +} + +func getBytesFromSegment(t *testing.T, segPath string) []byte { + data, err := os.ReadFile(segPath) + require.NoError(t, err) + return data +}

TvT$6NC{KvQ}g7w(|BJ1Wy)?iKx}2c+lg= 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%0ANy_Wh-KP;TRXuMc*D8uB=@< zeXGb=6@%Q|A3Tu4uM=T`9UZv0O3h+}JL*;!@)_nz%_yaKG+nQ>=(Vy@aAlckQ#-=& z1xroDC`Nv!ij}%;e*K&>GowvTew;9k5CmijpEC~^7lhf5KCrPpJ=rlaHtKMCm-v1f zzJ|N@9Zw_jIi9ZBHC~0^i`gv>1d%}QXb8;PzZ{x~yX0cZ2NCI=ogYz0t=4#`B;ulP zVXg+c2`g7MG|ILrW_h2^OZi_BY@Q2p+u(cM#Gu2GAfN)cwq=e#!6+F~&Zd>tR#{Ka z+Vb)p;Dp%%)IO76R3cuw)IFx2IVH<>)4nnrl+$S;j}4jJ(so46`F)8KB_)cbnxfP?D@)64Q6 zO-c$q*|)%_!p-aSosjQ#EH~F=^T(EQ6){5;?6Ox>QB5xB5atFO*~=zi(6m5J}f_&T@U&?PgAttJ^(0~y`O*G~lQS~n~6 zXYM%=@>MIkF3l5KcuZ8jeY*hAfVZz7((=cT4I<3R>*K$0x+rrSUQ)(gaD3K7v$e^| z(Zj;eALJ!)J#y;)vepabKTrbYD&L16aLlJU81;$J>hM)Ji;gHoKo+v3o zXhYx4yq6B_VLNr`^o@v$REc#K97`{^Ux-nG5Rap|drt2-1W zf!lQ(O8@{!!%MPPo?gMblYUxx@X8l=`YpkqtD-SSF4gy0UFyA@5ctW^2j8kXN51n+ z((J`EQL&@$OV53}wp+#IMr#@~HByz=YEipe-paSy#uEIr)lp<6fefANkR*Wnc6P)M zx!M|9;mSC-(8eWa$A6lds*V;Sn%_*tV)hd$a%VSc!T1*W_Jv&c_EStihJpZ5Sb$S3 z(GsaHDt%)|#EHi%qOPGa+v3*fc;f4WM}X-G@WbE?hKGj_JgmDTJ6h}pvZ=ALFwpBb z*$%OFcYcuUp_aS5dt6-Hw_V&?Jp|PozY72A^UO)YOV(*LVh^RB_fd|aUe-2xn}MPdA{tvLkUBXt7|&_Iby*yR;X6@l+o~K^BCktY_D>GwBi*( z^F6}f&s68ejWquk{@JgI&fi}`+{a7B$A>-`7D8Rkx1%)v|HeNz7i@oftY=|Ce3l?( zR9M^fW}`MKID$O`^p($XId?U;w-KMWI8~e6FG_1Xs3J`B^mV3n*0z*Moduw~=?Zbg z7gffB>vHP!P(LJQGgoTno|Q!6rGL;bI2OjK(dwrj7(iKwYArhRK^Bno`P z(|sB|tU0b(KgrG`Ez|RWtNhs$!aRb+m7>>!NcpAQ3L`OT=FJg=NK)bCjVuFbpKc4sLA2A1WkSy1q?z>vh z_^GU^Zlkkdtt7P~1g-b1x3_H%gOqdRaZ>as@xqy5=1{C&l)3-2y1#3FKza6%{RSFWl1iT7g1; zKauymcaPG;!|Lwisw*bAxbU~3_*c%eE&DB44N}+|aJS)=nen~OpKC`L>*~A40 z*a=6r!EtF}MT>&DKvkB5#c3@&Tu%@6h_v&mOmxW3N&raogocrfG(@|a+Lg#4y0k#~ zAu_UG;g&+t5Af7H2( z$EJXC=$uPVPWyGIy863IX@IKf@&8MqY@#xA{N2}!1amaW#*4jyqCh2(yDSd~&??mj z;$Nb^cQ1^DR?gSo` zGGu6TGvWBZbL?(!Hn5(I-+AJH;;i2^9|Hxg zgddBT zDU;zl<^AFyS(DPnFx{E{HawicpIDVxESll=)2;Uad&((g6%Z?9-KLma0PmgV7z#q_ zkogl6l0~}4@r}eaJ5Xi?c7MEk;Q5ezgS`?9SmJ`mpUh z%ufhT_aj;+Kget4(^V6~!6;6hEW&&;@ zVJw}umlIApr=FqRr~!0+J#}(W-^J9w@GC>dh~cMYMAqSyg`(J?PQn5xIsCF~FmZ7bgD=Ga^*z6> zb>-=ww!9fNx@Rb*B;<`fuleNHu*r9Ej~{k}6w(*|WNN~<`t<2*<;}UKk_%%pDHE;X04s;=`EYVlV+nP+xioLX z38bU)x3-Gz+!1LO(=(D`oB54v)>>76nI4dTwr(+f&Z(;txx_TjEXm@|ct}R{$l2D` z7XK}+Iv2F)Hu{x^r`Nde1aqb!;S#gfcsBpAG0&v#kat7jr+nL*pzb)Q;=~=u+s-#( z+I}~-4GVX%_(GngpUBAG(Rl-tX})f&jDz#D)WI4n1BAHuF{$WnNsv_dn3y;Y=8pKz ze}3b%)Taq>6E)MY?X+f9Mtyv_p^RqyHbtZRP@ITGLp{&N%F4>TycN*Eud{RT)xOua zNxhQAg3PR}Z5!4c{aFyRA0`L}ws-(3ExgpzYP|AXc45?RZmcZUy~Px%q3bH`oUaM6+r&W#u=W> zN4)|Xr}p;tN}@m>ZD$Yoh9{C;%2Jb?Y=?T~+FKRv?=uwoi-*R&{6uZWj(P^333>OD zUp*BWAuBdDo<-To*;Az;W2mCFI;T}vvS!tdp6I%n0;0I?wX7cI7W4N%2`gVxIKB5L z-kDHs`(k1;c>@bI7UG^m@oD=z;Uh>iwDg?!^Ga@3d&QdiAo9OymXPz-v)cGw)rlm@ zv3&0LSSC6(XOI>28^D0CB#+2cLuKQlQ4qxpg>s=}08k)8?*-AXcv`%y33qy)e)9PZ zXCk*HK;qC||FW=sNf`9kH)`;_CQcyk>gLwgwhl%dEr5}7bX@%5u!HP_?Zsaao9F8* z5eCTcSw3pP_ymU!yMWx@LKByPQkjsc{a_l$8g7ZhNi_>s>rl7IcaD~Tk`-zNZyD+Dj5JD>)-8Vdbn%c+o zX(Y{vNW*|gw1~{fKQHDJOJK6A;wfLK{DiEETia%aCM^#FiXLD^4yqJQu)J^NN)=a( z^_pQRT;Us9ca~J~Jbu7t^W|E*M#hrX4Lz3BDBHoVqTWi z;}f^Zm%F(h)p^Y_b7v-zxy3ER;XSY8r>brdJ2h#q1zk93;Jntn@elXw6)ckf&6lvFN`O8QW7xZno zavmhQ!G6g8cKSd)wT$6C26u$M<2Ce1GI!85v=+>d`wD`-xnQM52k8WPS1a)DT$&wv zW6zoQMz5anjW&v;y6~$wHlM<@>Y&PV~mpndrE5RfG!O#sF=L2Nuk$Z z;z9L#VbK%&Rc>fYbzWYVS(~=;&!45pV)!|M)IfS2tN!@B0E=x@>e(Qu^D9QB8cz7Pc(7A%>FWxcU z>6DZ*UYWz<+KOf&r3!&@`5UR)8NE;oQWv|pd7;?GeU;s2@$Fx_CfbG@H=tuX_c06} zb}~O&Yp5MarZk%rvVJ{psAfHXKAjBe%=AA6LK~cw!PF9w5D_?qT0N&h>spl`6zle(fzhqlvwE5L1|43#`F2EmEP~o?_jfUc~ z9MJo&UhyXUl*Bc@(Aw6SK(XW^gvivk^JG}gLAA1hX3hwN&(o0dtKL4H7Q)ubc5R{d zg}e;rV`lQOad`p@{!k}@MCPeyiMQp{x%#L>w!p|t=nHY?J}(WF(@^iOp>^AYl(9;N zg-bee#o80ad|i&dmCb+)eiXpF@77p_aWL8tq2JdfKE1E@-H_MHgpYk`qZ;Pzb5`YV}Sv+nl8@IXN>q z8Pne04&e8oGym&8yxTO(m2BJ!Dv{kC5CLAC{*QpD?_EvlJK!6!s+n>xD+swAJmRx{ zlw}PiXnSz1J~M620-FBi?pCnN;xhCk5w|jQrDYBX6l6RBA&%e787#QE99n zRfv7yLH^0)7gGMQk<-fj8cJVAotbg+9FPGBDrrd02R|?l{BSafaprIR`-2B0`5QJR z=HMP)HL@GBuiTV_^D3V;?lK# zoK(WF;kVNUfO4V~j(iLHGyEkar6Gf$(D66OMRIfilE3zC<%oS$t!v(9?%iyz@40XM zM>Mk=`zwdWmB@AkymiS_*6b~u>Qf^TQ>=r5< znaG#yr5!VJt8F7%^ljzxc8pfuv>V+esF{?1C)goN*P>@Hj;&cI(u0ObWYlHKAP}Uf`P?#n+ ztnAu5A3sSpf(9mDI?7}Y^T;O}J`@uV5C%80L*izc#cK|Q%=zN^m6$LF^xpsy1cYz@ zXeNLXR4HqpoQ6ywvMjPdK-1FI1VF#eD3MI4IB)jAd|a>kcn~th!rsA}4Z1hY*G{{4 zL`-Ea$RqJ$T9q+#(N+N!UL-G&iK6J#HsvBl$9yfIU zq6gUBoh>Wm zlJl<3!Gg&5lr)+V;S;2+Ey4PJB`TCed5)1#-4}{h6GvNCgZT|IGgk#)l|mRXcrq;d znV|~1FkgC$ytx>XrS%ep+wvdptPZ`-9^IXiVHIS`Q1~WxsH5Z@Q0ts|%xc|BWgN!A zruf>i+?8Ubg2*aLIH6gQMLPOr<$H)RIf=?4({JJ@mZ3k4pHp-7DQ2bP1u=28PndBb$Qc<;gHvI zAUK0zM3E5MDcNmQQ5OnTJ9r`X#4-=2GMY&Ubxt_eGTTp(MU}hkR<_c*kR~tdDA8Z_ z(eXNAb+Id)Z+n!m>qBklaO*)GPPu3WTj^_ z=9)cGZcz1c^o>ixgd)6VN*IlAU`VPMK7J{KaBV2eTsY>3Q4NFS)6`%uBx*5xUxbSv zabZ7!G+lV0*9AH0p%s;(xJ|~b$tf;}KDD&H@$O19&&XN6_u-m^35ySxDMp>)nU|07 zn&oYNL(!Qeo7bTr+uPFfbDg${he970mpC*~X$<}Gws{LqK_3IdVO)FEBxqX+Lx)x6 z6IkElc0m}$@A_a>|C1ktTx$Pn3T9W;X zdq3TBG8C#stSU{}TXo-tmcJ)jGOfg1?K&OP!3uO60e1$3v;NS_$Ju!ofP@VV4M7K3 zI~#Y+_LD4VlW+e1i&m+FpOPNZ z(eT&rUPF!-552_@M;1j70!1f?*KA_i@Cy}xa?79E`sJP!5@2fDSu)ZQ|^Dk%`;^Q zCIYk8JMXw$Mks`MvM1-tqp){?J=SsC5fG3z^mKfAx4{eM9_{RST_oq-JJrNg5{wep zT%1uw@Bt*4MByN6@n&c1kperkCRXN9%B8b2W9GW&^XqHyF6gZWnFr0)Y$gM&Z$g&y zd@O@50SvQpFFz1t~gWA_gR#W7wK1lCPxwy5;T#PuP|T}v1}qh=#hqlwl%qA zA;YA3o1CCz!RkM9&2s1wV)cI5)i$f&*9unxImHCJ-tfQC(k?crdC`Jv)7TnI#PXMC zioF9^8)W2cg(nuOA%vHAK=nvr=5F35fN7vd+&J;_!tYi@-SEDLQbk$lf^K|(%K@e# zdU~xN9=Zd(H~2pQegj|@nGq&56f{A&Af;i=e2w!hwEi0iA!RmP!5@b8Ql>|2qH3|_ zT3~X&>*b5P@6~_}0eB$w|O&-ueA|cO!mrX$j{O=*afu$rJrirc|DPilaR0IXUs$ zSp&!EiVu)@M_7~8s=SrM{@sCjK8L7UI>J5#10RG?oELobMz=8NIQv2&jksg9yW6HM zj4U-Z_5Q7|xqpq>KelOw7$E4oCF!GHk`udHT$ji^t1tpk4NPYOXBZ|ffIfa;{5f8b z1y3n$HaMx`mILD^mO;;W6bu0-#t>aH^XB>{tChnIoOM|nqpTLXwM9ivz6eI3CpTN=l7l3W|yE^K8>Vml{Ufyp+zYc^#KempiE&Br&l8}1?mj_LRA#YEJp zsch7#XOf*s8HqYuuhtWnRpLhjnGx!Yv7Bvg>ShuSS|#p{iH&E}$0;o8Hf4(lgx2#p z=iBtj@-=Mvj6OlMvKS~Kb}I-tm}6JKV87T{(?UxiYyixhHTZI|sTCHBU6lWxh)H!U zk)U^Wp)k=dH%suf12mnmFRYhzh}K0KHCu>&Nz772yoT6+kX|fAE9pO{|D%nKGMlUT zG*0?$Wn2W+7uQkU{;n09>1CWDIvM>CIvJ4Bz<12LZ6em!%(^xul4aovm}Y+wEgdMBw2!T)<{ydK6 zO899{N+HEjOs{ii@v$KviFmmtkJ(G_E5^g9R{)pzLWeWexfu{B!Lue8mbmcucF1 zjV=OHw`#jx@#VTpC?tAi&Ci+RzE7`DPjWV=i+ZHfHm%Dw1pT+B`%<;{27fU`RL1c` z%&X(bAI+-CFmgM7Gt)T*tW;-u!>KPTwhNzAFtdo_P&J}kXm=TJD z?y#C5sl|1)bc30Ij77jw;YA^n5Gnp9qOsL7&y<&QY5N|@=j7M#9T`S8v(*QMWCT~% z#?!YqKR|Fw#};{i?bD2T1)es=(*CH^<QM@^f7l7Bm=>8f%2f(?k8U7T~=*l#!w+=wdEYn~?Vv5*B)H=#8RGDN)1M z%0;^tV%G#gSZpt=j|54JftHot7+#qV5Q+E@*hXBfI>ICc`s*7JG2`3K zALy~<7VSE^Nm92O$kz$2J*9OiYSm?@v|;n~3m_7M4#riB6Ezg>^eFS)Q3OKn+axtlFdK7^kfbjyczd!ne?bi=$Uz9jSBUr7+=X8@<0XW3>u zRz@s4!sU{3;a8!%oF}=@iibE|R`W^}#QNww5Wib*paVaSF<+}(3~^RU@jQlnikN+7 z9GP~Xz82|pt#$nP1xs*PTnJ29&XB&3I0qxU$bxk<3S(nv&_8+xsE#{e(x_JoEWxP& z>;U|LqMXPcYf-EPlS^~~{r5mm;iSw5_=S|@ zG0kpV1Cp<~TIb_GVxFPkSIC$IaUqnWqWd&L2=z{MBncbV107JVaDnknSC0AGc5u2u z+Z(?nH&<6MbQvI?K&9`ZLC^nqv-%OU20hA3;jneuy|%nf;kT8)_?{Q$P~zo1bcC%* zt#XH3(X}1-P{!~<*eX0z7H?X?flH=0W1b=z0F*+m6K8CvvzwbSz@)0-T6Bf~*E0ag z3_A$c@NhCf{i#r#02;b?@2{62wVF?bvbdXlW`LGjHG_v4d*iiP8HRWNEY_>U_Jhl2 z=>;bd%nr~h)VVB9b;Tg&|5DwqSPF}t2Fm&#YVmINtPOT)!0qp6pLP85AK?$62i40> zz(sa+b354DdjJ0Yqdmbd!*QRhy8HXbTdD*8V;-^H9flXdn@XYH^P>q?Y(ARG2=hZM zpE8w?Fab|S$@^1K;}~AjHNn2Y4)n-q2#E-9YqA)3_dFjk(c`o(3w#jEn=i3TvTdFM zIA{QI3@HCM=ae@2nAWkZ+!t7f=ED4UGS`5lRTlyl4^cTgU!U>N(Qr%U_BpR<>U_rI z5o5(W?lUQ}?J6>pM0p~#XB?atOLl-S~5A#-J=!va%-ygpd?nEG9#sJAeP<;T3d7)d(FF`h=1A&b4GO z|C-{vgSoQdB-65c%=v=Aw}($EQV`>fLE`yQR~8xR%ukpjpiGaGK7i0lJi$7Z*SXtr z)obh9{Z+;LQ{m3u$GWS?Z&c9zbM|w!fpsK?d)YPH%VQepynbiqc$Ny{(&aJzVqXoZ zl*Y4LujuB$bHpumFQV_$^N6g`oF06c3P@G_yQkMS3(2Q*2Zda(-}l&G9?dupus}GhK!!iJ`)WS=qf@Lxn zL`Apgelt~-u6RqmGat7|>j;=XQl)H}-90yy7w$>2JAIZ?#B zLv`(Pu-mQux8x5awd`#)s~UL=kZ&P5b8`s~jMp7bZ}88``KrQyt26pjKq4l2D_&oN zh799kx$qlxo5bfyc@V{MeNjRy*bqI`fn*dpf3f^l$Wi2E?kp9?K!u{o*dlCBsVUs# zc9q7A3U>-8^f*D-X^bf)A}61}&zci60#Qmanwf4*eWdXuv7tbJF8;{}SQgJH+iHGJ zuN8*FJ2_j}?B%ftg#VpZ8XI)gHKav^&e8nmiFNZjlq8X!(p$-!Sx{Lw>?&eX2bGeN z$f-XzJ#G8FDt6T#mdpm#u+Q`aZ?-Hgy_egoW#6~RoY?ANUJH>hZPRTlMsOiQdbJw8 zV|5osxS)#($N2FOP4tH~QkI7Hn%3_+8OPKpD$KJnuD;EjU72j&*=Akgr)fFG(;s}~ ztdtc$w1{NB_%@lG71TKfT0ASMP$NxkxM0;G6@S-AVXJ`vCH$xpg{AkkRaUX3MfkZ5W6UVhzb zmJ9gdYnnamq9|8FR?d~|?`8HYrm^+q)vb0_+0!_?vs7F11&U9r*I0)5bZ{Wvxv9t4 z?b0Ru49_OT$5eC@9f2my^sKBMCiZJ6M1i+FawrZ0UW|+J-vS93HRKgtPa(mKObcaC zXF^L}a;Xu+x;b5fbtFbbAUhJ466KLb3-zt2oMM9+4KAmAw_ACM0{m?rVKoKCjX4_* z93e&vaRYei_7>WL_Ucbfzz-^|XVu*xt_r68Q9+RSS3!L6yvUrLIF@|3DH}H8R;wxd zHOA5(Q^Jn?PWUQiAy1=ep-2@Ai$wzNdZGsp?As)8j=0t4 z;up@0ed}#klpZvm*kfR?SoDXm3g$wn=5r%l>3ZjxS=#dAk?qvk@UUxrN7UKH1$#58 zO3`=W(x^`q`>)dy(f10?)(6-7a1cE2GS{ zQMLgzZy~gJ#`QuNqco)I#;sLZ|vVIy!Qdky<52Vs*70ei+mhbA;pP z$*Oagwtgf0o!P?=!gsKG5W9p9!$GxC7JUa=6zH>Sefifo9H>~KBtSE{Mb-8u(o3OS z;~Ad3E7U3d^1Ds3ysh(DOWXBvWncG|wk7JhSLFgCH!s!=`%1Dji;%y>0EwP1}l&z4G@BG+;MEAj@#v#4HWr7##Zoyq1Kh@E&* zQ4t^*fx7>vZ&Sj1;V`iPWWo7fWqb)&xWq^AW%(Qb&I?vY#`=#U%WLov^h7L~VxUlg zo&U8~(gaWX1jT9VR4w~H6iw)RT(Wew+9c$=IG}bv{IA$t7y|T#=&)mCi9&`gh)GE+ zY$tdK>WhYH5l}&M(R#H%av~Kv+r(AY)T>_|J}RXB<$Xi zU}R~bt%upUxm<>|8jQqAMyTAy++&|()tRJ2f9OMF!~MyA-vqC3F2BY+{@4EH3jN!^ z_Z-jkm%hx62`Qs27IHnAr?^5${*0gQ8ykUrPd8i(TJ2BA+6L^hmI}>yO$T6CRQKia z@$uMz$L?}8eyi2UqH|V(pgz-Mq=W~uI`)+N=rFd7)6;@n zJZDuIE1$zJ|L)p*@z~f{ApSs3N{Wj3D?U0v6O!A+D|ph`|KZ(i@;uDKAQsPxzYUh!`pALt1Gw}SZq z`AUmDMLkJ?5(@?|*VNVmca=*ArL~Z|_-A)4vl_gr&%{ISBVpfZI*Mn2KFF?)z0f$==NkYO@=u+b1l|{NGy^)^ zq*SK_QdBMEouRxLOFa>Qw3#)i1X{qqLEzoA=r>Rj!BlLG{!hk9QZ z^Fx+m=nHc8Ay`2cx~cZ)(duADl$fz;BJDAs0$(YlH@(#J`%lI60-$$;_FK`(_6taUv)d-3#IH zTODQCzc#n{R(yYb^{f_h0Bx_ot}ZTqE}H)PNY2E+KdiXEC+6Dn)iYgafPLuXo64?( zQ#z;p-`}pEg5M&MIMkTGe^UaTM70j!8E=Z|y(9nhsNH_-BDViW;@m26Eg?HLgM60r zD%N~`34I2Ig4%lH+3{M2v2VvVS9V|;C*N2CrEka22;&liKkX7|c@HQ!NPm4*U~)Ms zYbR!6jKSM)uF@9{--httmp<_C9)tl$h;%i|=}#vF8^kh`wjoFcIZDaf)dF8H66eO5&1BCq3XUDL-M9{V~Z$hkmJQ(F1k+?r|YYQR8vj|PkfJy zA4fP%^VvEb>*#BF_G}3HG&qiO8!L-r#K%$tFHSkugZrf4aA7XLSRr@{-x5yO?*42d z!#}-AVfi$!zf@JFK*OF3g-mwhT4$pmZbPz|0 zc@VL)u;#C{>@@8! z?B};2NT+DNqS48IQ}NMyQ`GInY1GEy;N$~)OULI zzo~5caRrO&h5j`5+ClLIDeNg?-b~xpr&^88NMF2`0R;=M6nxF-=a9t zD24S69CT4njRmeBJ@nZ>C^kMtl&$-X<+|yi1O@^twsH>|Z8{sd-YK1Lv4 zS-)cFb^N)mz?fxY*KKM*JH_um(y}v|ByNbZ_`%#7Dv1i=42?ZhXWY_1S}gkXvsLa@4r!Qtrk+CI8%b^vM4~GHWc`LvMQ`uA zw~6e7w7u`&`w)owxVZE}4j-Sh(udmWR8)>rQ%q)_uO8y!tu5z0ptj5TAVNCY4SgWu zHnH;H!6w!HIKo>3(JSjkaC>eBhS<=8{IcIW*Mx*h+1P?H7_UPwH;JZx$_(5G)AAb0 zF&|al?CJj$7@R}Y9a{j`Bt=p6hE9vo-3xRPRNF# zKEWDl9N_tuWozo5zfyttOb#`@ieB19^p9q<8FvxrS*tU7s99P1eQ8MvVf%9`PJln* zv)FSMPQj2f1mn?#Yin&I6!_tn(oH73zi*!&6m>1&J-mko#5ee@izz#||_yRCmR>udX``&?OtFbq=f z|J8}H`q{H-7V@uMW)rt*G}uXtNJpV+RTC@E(Zj2l?4>&*{=d9Lc<6n%(&d;RMl1AX zX9olYcq!jpEw_|;>L)=sP%#Mqe|r1wc(&U2|EGIsjasEtwMtRcOzhUET2ZTNKUJlu z5ix2%9ctHzS_xXScI{1vO{^G21VK_WB}VPvZ9JdP_c#9j`6pgEIrll|KKHn;_w~Ns z7u~)AcFuv-_Y1863}Fuo*tei2a-pPenpVJo6srBREe{HD4C}6D^x)VQs`LzeMfi8F z?FELQMz#Po6sCdWk7Pr(7QG9wyA@1Z9G2+j#&jJP;*3mep1&XrXyPV1&hL-?>P)oT zS+`e{XE))L+pQ2db7;7&CO@5-sT&nJw4)~8LrEA-D=D?`ovyvYjwPBs;a-d7H1*fVx&TJOhNRgR3IjK&1~f$#J}6MfnV8DD73a%CXjcXF`)VO<^q z?u4iiEze$gxuOjOch@(z3(|Y(ohu)IdY%4OH!{M-z)qtt3^{1E5xCq(tvUgO9gd8z%YLh0za4PV z&LCVYb3}2~%G?(-O}`C_+^_E`Uk_qo&&&8UWO*6F*%?o;ByCMhc-_3o;_GYvhdipM z>!|=QIJ<9K6p?2!8}&Ze=%ZJi1ZDoJN%WN}#x*oE1_ohjw+FSL#do!zLR%lFBUu^ftcX2+>pn8aqR5f1Gd17SG5x z4eBsTxxy)zcAn+yOgB?ab4x$9v@CL#8yo3zf9kpSihU}=x0LMIT?tZPzJIj?`0{qr>@CVL;`qB@5Y+c?f6jz2WWSl|io zQ615VRA!sYA@`~WM!JbBOG)bIcszW4uUHl#a>Xx_h%Rs)-nMXd>b4Jr=<{rSAtB$E zdEpMJjUI-R28z>s?n$}=vCCyxju)|)T-tv0oBuLR$S+K4`$)-dS`UiI!LLJ2`@LNuES5$?qfbZgjZH(P zWk|H`2R`{tp;t9wQD^vXHcO}S!Zr;IsHWJXW!~=g7b~Gxc!JidZq2wQxQ(c1oqzSi z__zm6b;*pLlusDaw}pIiSx)WhJ-=j)i*TzR^O@`EB_PlHKFuu;uDp)KbiKr{6ZWsR z>6A?^KY75bpsj;6+RK+W=SI`ApLL#sBU5~@r9Sjdj-(@zia+H@FzzD{Cn1`Rhp$;M z1n~*HX5gac_-@d!_fDD+t$y_D%HVliT{CU$B;p>T)3a&Bic6!VFoDH-%32suTC8v` z!Ia^HTxk=~yU{hTeOd(-wesPxp3#8k)3tyI8cT0YPLjG`jjr05{@|amj2H>^OnkSb zlJ@natVY$Yx1eK-t$i?|?r!_2MoIN7rfJU5YE&TYXs@zvl%#RUP}=AW>RfqFP8X_= z^ZFi+s`j_dNB>>|@k{gj{au$bCvbZF$iQO%+MKJn)Xk|!S+hRq4yIL6Y-6Qnwl^em zQxi>k{LHPG;F2;3Z9U_7Zi?@@Ef6-gN-i^<6$cn^Wuu{5Kh}J^?5-SrLJS>GJRUFM zNY={iKEOJMpRQ&sFWb^N=*WW?-VpRN1)5plz?aNQp8T1c`JNCj zgTz2xWv#63FK?LOYlSpV$l3g3ZCUq8e^n!H8MkU?wO~oiH9cDS%XH&`cVZ$2(o{w1w*m(+iRMI>}F+}eElGp zhTjI2gY@;!i9s}P!Z2=JRv}eQot)Daq|ih~12NwZ-Wf=IiriCD1TuOyk_w-a{wL<_hYda{*^X#Jg*Xq0Ykb#s>L{rzwar;e(r*Qyu)GBCynukJL zqhm>@=C|6z*VPVClImQ%Wh1D(abTi5Jw#LRXom`D{L;^567Z+J1-9-mXG4r_qc*&) zV^CDgnOVVOA8>ktPJCn4szUZsZ`(g`jJPdrc{i#OzrHG;-huY!xfh%(^#Z^UP}Foq9)HsLvsQ=vK|(&B6*O zsB67>aVgYcI!Auh-L$zb58I2v#v^X; zY)f3Aj&asTGs)tjQmN2N=Mb?I_KaKV(~zA$=Hu{CmPjIL6{O+xL!n~=`= zABJ3DSfH-sYK9w})ufiCjMNS~(FD%fv5eEphi~Rv4?+dnHD}7Qc9eUmLqc@tCSnqN z&?a=&?(Qhmh{}|Rp&;fAKrC%WaA>ecMnE;c9d@BHEy+ZaY}6pxmEjI|?glW4>RI>F zJCH1Ck8{m3KiA<#57A-}_LH%^*}8&{HB*a`CkSGeVb~NL7cxGc?eZ@LLFDyF+Wp0) zWOP0J5o?CC@hh?0 zov*y{uY`Dhp&tb+MSGYx!)KX?L@E&t2qkd;Xp1yvLCKj=nhFfo^tj@(sBBI{;?K#iFxGZdMx?$Y zx~j|SPey!#skwekFM5Y;dKQP~GvL(OfZxP)yR6-IRrn#zF!GmBx@T!(dj6%%yLc@j z`)n;ej*nB`0XB_f(f7JtiGr}@oG~38gH5eV0tTP3DTEp)PMh^bA%LlVUdWf)N84?@ zP*?%oL+9E;moT5u+&V)6$o?$mh8LtBct=^R zErMen&!wg}jFr zg&nOU4$EYB*l>!0n!1KyovF|U1u=9Eps-h~$&JkN8wb%?Hw@;Kfuk+T>R^&D24S{xy3sPxl8x|8`*TF(oOX8{oml!P4} zmDfffl~Rvg1&(4yo*SoOuIC}POl+`WcMuUBh$$UfN(4i>#_*Ee@Tfx7eY3=0#a=$P zv&Z~XzRfq1vBQy4;vmXOW=Tndsq4)pfCXUg_GMDt8Nn0^i42bM}en$!5NFq*~I=Q=FIQuj5gX=vcs%!C}K?(E!TCf`EmX+&t<$F*Dr%oUc$oNcl58fbe znsBWk>AATseg=_h?3)ItS{1C3l;4<{&C0B#MJa9X?a0w;$i}8al?!0>Jzub3 z%nYg=8u8j%nYn!2Q>)9B2c{$vhSE4sr8FvX&Wnfr5bVx4cht>*1d{KG4SeGtwXq1? zJ8zM(AIEW)RyES~=|<6>_nm%4kn&AAl<#}?4ojadaCSi)%FEcS%usq|GIMt(69-sHf?$AB2#a@XzjfB zuOb68{QRHJ$6jT?(VgiWn7claj;^Tq?Xy)@Rpm>|j;Z$D*}hfzXd-6QguvC!5Y&0< z4Eg#V&f5o~qH@M=@uJ17gt0?M8Ms!Dza^<3)z+S)T%oFYV7xd1!KnOwl9@g2dKK&a`oJ@ATyPyXcu+`w@6sd93N-*@Mx~C4gpo29Nvf1h>M%5 zt8Idu)W*hVdC6l;$1$Oy#ouSLB8cnw=v#~G)`t7zHDkw}ljTMy2T`c=+`*)!4#NIH z%~Ul4^eMX;fSQ6{)6;~K@ly*-B5^1^$5)<7E(|UC!RM*oR#pxj|JD;h>j>UbQd07} z0J55vypFNg6AMB{W223a0}MtzRsoj;avm}`FH-0F=qS6QqQ}zG_WXPYOZRPoHoHpB ztMS8E(okUF09bJ6kt6Hv&QnM6AnwL2%^0Z$8#4zHWXJl;i477#vCixd(d9>_>S`9z z(DB>fcD|I-8l9lPZFh795L#;a@RLlh>VUhOA&K5N2MuO@{bwc0N$g*jUW$A`(MAhU0}CTMFuKUm{Vl_++G^ zfnp8k_VViJJaO&VF_vS${Hat5OxwNx{H_uRMmT-R%*iRNF^NuKRivlzD`-HtWC>e{++aU7y6Um|!9$tA`|_Dp5)XGsu+&}eGOu;!#l_^8p1!QK zzx6dj&DGkTeEO~A>I!6LqB6A{4auK5LG{Hm(x`+setwFxGb}w#sj1=Q;z+rFOO)Yv zu79Y{Em;Ms?&>Jm^R_VeKbmqEW1x0pkiT*(eKD5R>VTvXlH-XGgbTWq(;gs)c|p34 zdp$02qXg9K)=ZtbG~d{O$6ADIrR3B5)#pz;FuC+i59$*?Fb0Vk-mi#>c9D^iitD1e zc44&Ioj>xHb|SF1PNwee8Y?K6+_eRS4~X%QJ#%9 zYDZ{AJT$LKlV~MHDeJtGT<`Q2)i)Y13d(BMr=NW8^9(<2p`G+Vdi*Ro3ON}>dCwja z7<%FQytGS;=$&r=TM-u~K2}%X5Dk5}N>eP+8d|=7pNKkna{gLJSm4hs4CECkwR-%U z_Of(FW~S}c?Nokci=OGIn&IH>kyZbHiX7m+5<~uCs1IQ30_o=i|K~N7 zH2ZS!I*eZ5krn>sVa`fJws~#U2G(80s7Rwi5)_zA%XDfAU1gA)VHistyk~5?Y}Zc7 zPP=Xs`GD(^k?hT=n)5bhnjX7(tis=>M~(eY#v*qbrQods`xnGb;lN~Uu<>-zp+AEB z1Yjp%j24YHL716$dXCiM)-~0etNZ(H<*&=lm6G)()u^WC{ zsCtbWq+H2f&Pns=h*nd&Ie+>3gP@nQbNsFDw{&5q`u8ZSugk6w7VpEyFy900qds+} zv{pCv-EA$YEVwE?306;Fez7}5ExC+2yr0Dk9c^o6(2LT|QF4fuU|e6Fq-@L6NdS|= zhexC;J22Ivm09k=dI+sO2D|uUdAcIakKY}vK}4{2RYDG_3qJ#)7iA@OuAixF4gIt7 ziaE*eeK(?LuJ?Ej#6AM0e8|&kEyc_X5Do!bgGK-`R97}?V#-~2pJ3|WmslBEl+(sm z+MuBep$GISV#?9R$kS*e1oo+Ux6X|4u}5`z#eXWMA(>sI1(XRfL$YC?9D|?(udNLO zFFuM$coC*VAk$6BPg$lam;6b4QmePZb8xp!EDzB~W`orXR}gF(cDaB5orp+^xA%aw zWNq`L4=a0Z^`}pl2ue)hQ2QW&*F1YG#EHtiZG*+4k;_D5AWt?8Dfm zQb8_iI5w&J0E2b5MO68NO?ub?+{;-AZ+2M9Fz>8{Ec@hSIW_yRwoVik{CXKLY~&}p zuKOvrS;`r#ri;PTd#022iHCg)oO#tj=GD*i;7?%&--7tRJ1*PQetp~;uM+_W2N28aBszXo$Nay5n zOL>MXU6Kba-3$*IX(pZdiK~AY2x@ASL||sxtOGAPBP)?|LjudAm5Zx>8yr%LM-~CDC6e;WEArgO;yp99Ah?p)3IXM5FOo`6;{YZ3&TL z`Ct6LZ~5aZo)<*xZV8$}(aD&Chz3YB4?yj&yl8zy+j-#j!d9&?Ej#=)Q_800f~AmW z{if2o+HQR2mv452X~eF#+Iy};U_VT57LSkZ&IILl}A*+DOrV8qh9!BSfqjsVq1+~>zZESp9T8?FE zUKtB{Kg{U-9Jt0?E3R79!fxeDrJrhH7x!|j&2a=<6+IT-e2V%|$WL#0IWc7q+0+&2D59N1!^w)~V1%YRo zuXaVstk=}Yt*wtS-199WR3||v+=(6E(C7!B?v$l*eafEA)!?=?J8o+FY&P~U*^B3u z!}Ce<((5fUzTS3DU*eNc0PD{l9w~^hv55q|qMmN;{~c-Bi9VzNK(tCh zp)6Ulc#qKdAZsOBBW9#uUq4r;pp! zFO-OmU(dA+21{dNiuesWsr+A;lmrF_ngX|{tyE_$Oetk1^G&)(;ld#HzLfGlwpr?o zSef{cZ!FnAtxBl*eDd4X-N*tT?*U2ay~wyInZ0n1vBVFIoJ`I7dgz!|XDO-k0ReNr zq}|!)DPs|_|Il^qNhWQt$XHlMBs#W+hT=R9f<#%_RkxTjr>5AnRL@OQWB~@W@yGkV zuTeRLYL&U_l#G)R@94KIJ8l$6lBkSs@#Up~xwSYo@P ziL)vwrBva7d~B6q)r}j@#9;nNH0R)OL_|T*J)gA~4^)3!|0OkiLxH)Mkc-#5V7fr= z$f-g{B)N?%Z7+WPDulz~Y0eGB#8p7}`D?QI($lAV-<)AQ-zZS^Gkj*v4zhOh?*__P zAkUtdLoX#Rk<+d2sr5^zFU7SxMw($z9+Hv?4j&Pb*k%yWW!)UUVb&28!<0M=QgSZm zPwTB_P{R|FP<>MZf!F~>C)I}5ZS~s%0=2GlF&H>-n^!^*!0*U~=9$+@4*O)~Z(Bav zythlo>|8lo?7yh78`4)I_`7iS5K4v}KEW&h6LULNNoZ>HIPV`RVo3b+ z^2Z8QUTbA5BivSy5-~Gd#mSTmdjS1KTzj=wC)BIOk;}^%I>vjKIm%Se@zJ{b`E+GC z55YrDZVpWL;P?2O48GXjC{Ym|O>GQ=D>Bh|{_+mr9m35o5u|!Ltz9yafk#>PnRRr> zpFe+ld4)YjdlDLsU3HWVFBcSa(^58UYZ(^@#0y*c%%HKlRu5)wiCokJawy~ zm=4TDCoa2pvPT8vk>W{|`WQ8>mT3$B6q^Bmx1JzfpHcl=?>xOwZ@)MEdOnsYJ$Qe~ zT~)a!d<5}L6P2?+uIwG#D&puD$@P(UMQ=U>g{l<%>Xkk{Py5X6nK0DThViI9ANum# z<)v-&7Np{%Pl9yXki_FVzQ*BN)jSH(wLlu5|WuYrKY7;N1OVk1=zjfczxc~OM zo?`aZ3v7|M7zs*1^&s{FQ|7}ED5ePm`Z{~+vECGnCY*+ze#f#Xv@*sGdL1y>5KYSN z-rirTs>c5;-4zmQ4!jc^r5<@>^tzs-b3N6}tD12O*kx_*LMzYBS9tt!|CFQ@%Xu+P z=1jVR<5cxImYiLFHw;kueo8X>rlzq=v>S%FE=m^iyl=A-vhIQdmJgaVK#j z1dA`bsOh&H-eHcG{~{_3eQ9u8Zf>$zc=J}lWK4Gn3TqvjS&e+QZxNl5y2#99-E$7E zfJTK9x#Oc#y)YG$wFMuE%jb6KNIP45*CQK3-#_Xy8l2cY&J)m(Rkuh@Gse%``kS)# z-H)7+(2GP=0Hcj*UcvJkb8}I2G`ze^@9@4blSGpF)2JI=BayAY9WcFHq(PltKc z@jDU^X)fbD(I6>jjGotcq|X?|{6H_Q`3+tN;e@rw0h4U>BzaP4UKLFtX0TL8L=S|n zQ$}d%3KhB44Q{a#E4n*X(9YldY)o_&r`q@(_cKO zk5Nn-$Kq{$GtbpP(b(OhM4f)u!BAXZ&964gY45u7WOCCi;VCfXL?8hL9~hL8q?*TU zs>VQ%<8&<*8f$*G7gI7G^`YSS#<82S?9?r#Ar!G4Zxk<#5#nL}q07(X)(-o3&(FoD zYfcg=^voE|Gxr2Mi0!BBp1a81Suf)ZMi65C()!t&%@mR`>4w_cs5nEU>GVwtM&_?Z>6O{N&L|u?>ER=UvMdZhi@RaNB<{iOb_9N(S z5k(@}cfZEO#Q}4*@C01&A5=yTo{db$jh@=ru#af%WU0y*tv?0h_0mj)iwfJTp*wot z&q}EYR?T!wm+7s(qW8ieqF~13m2JKxmU4z-D-$0N9o9tWx+bzn=|LD5{>)yi92y_l z%D3YUVV9qk8%BoDYErp#>KmL-NvTVUtH}R`@d!fJoQ!&zEj=UJn5Bf$Ny&?rdAyN3 znV3}9l(nG$Q5iiIG^`2qO-OmVy!yOOFV(N{2)&|oU{-Yc&)CAF3atHB64vMqBzAHH zb^2*cJN9;rHZ*hyB08}C9NHP$o0+UO@61y42wYpw3i=m z7Q|#SW!{yu7#B4^1JNgNi3$t5iP>sZianXA_P-6-C-7mzckfQ8rmny9uT)Pz1-Jtn zTaG-*fE+u*n8{cvc@yUGba;2i94I%(#eGg}PHfl+(%r%+t(KPQRdy5`T_fui%tga| zB09||Q_+1vb=R-Pi3gg)$l6+v$~(LU4&WT5FN56YU1+ zdcjS>8MdayddbjuQvCoVX61|gKam{e(IGp%S&p*C=&4dCgB|)_O99=3)Snzp zc%}#8QlEW)>EyxHJ_sG-+$l89nmvJ;=SyGyrB$V#6}jPXrS~Q*Smy#Yeyo^8;keJ* z(V_`Z2nF<7^zd-I4E|jRGbo1WS(=iP0*q4;k>`W?;RR*_pp%T5GW9EmoK_ja*HukN zXFTw#VlebnLbg{Ja<;ePeJ2wSB2TuoljV0+b--H8MR9d`eieFL%4ELuKSOv zRR|(e&%@Ew=flnb4iFgzP2+QWt-Cf8q9(^P8nYp?GIExcSt{sF^RvqHe9{!>zdvR% zk(jb(OS|RfR0^os-TV6;Z>uH?z&yi{-oIn+=G%pVNl`kcAFE3Pl23BQQ^g#Pd*>*k zuP50S>LOA->z)wUl4GGf+)aQ=%n^GnkOd@5h;8)(9LsrD@8#d)Wy8aKK}ybZ19^6X z`E)LF00jK*uP0P+v8U(QlpUbx3pU!^cNj2{oR}v+VHtgShnk%BEHm z73*BM6b1x>K_YG-(rnUFu&QbRn#T(hsjaogNchP3DCX5x*a6B5-cMn{Me;S5(u}jd4uz684u~4ghi=j-+oQR(# zvQeQQPnJ}cl=N-=c_HO4n4dwc(&JnB_3PTN7mkj8Yx67{`pfmJtFBL;RDH~+Dg}ahcIYXRVCA^Lrt0p1cSEZK3h7&G zIlL7YZ;!|>kM8z*cbYeAE-H&u?*@mBNS0W}>TPTfnoP2pj+b$+#cclgdY4pD;dEW1 z5E{N^`T+R1Ru+VSm6gf2sQ&&~AhrKYWGL(k%gg3_a?D_80|Hkow>8lPtP?B&y}CW6*& z^8UpMD-f~R7$Xvi9Ddu{bA!k(13eQ-r?2*1Nt&ahd9*wha|AKRRV#H(k!13VKB7rG zhj^NY!6X9o18r3lLeF;yj;=sjQS zwSMw-YPFk>_k_Z0g-;--NBR=U<#~n@+4RAx)pu1#bs;AOG{um}E7*DQg3C;dDxif8 zV`hp$K(2PgW9toCMn+EXcBuZXS&fxk;HbtU-tqag%UO%-Nn5R^l9 z)*Z3+z~oxOFCpdCjQzl2!+3v_K#1rmHy$nL2xo2A3`B0tfYR5Fj@I3h z5)xJ`>zv^q)Y3shs8+I^KUg>DGCw(vp%u}dm02c}E2drfaHAL$>lJ5@eFg<0d(~*t zIj2%Rk)JTX=x~F;$(-$MBbN=))Ty;O3yIg3sT&x*F2H-~CQq}9(!8td&Q~l}nA@uS zG27UfdG5oOFZM0J>=n;9t}iTv85*iiOyKw9C0>pfs78-s) zh~|F*_%!qGDGFrZZ{p_wP4zgXx)C z92d_mecU_d%Ru|(hM=Gf|J-Y*NBR1U>#HV-yZM^*297Ym-r0BL5}=0967HhW7Z2Y& z(It@}_7AG9cGiHfp@J6Rhh?1fExqe&>=E=Kpu4f>ldQ(NG#S0jr|xbpeU`7KG)8Vt zvc}p<{L)5$PC_GjCG+}JTf2bn*A3}2(@Wmol9dDzpTYShg_Q|kKDaGg8BDN>dFVA4 z@r1#eK|Hkre;~rb;sqI>7Ze27C7gr^G-*CU&&K~*n;m`M?%J>H;h`pFX^9RCCk6N~ zEG=#HHco3*yZbRQP6Jfv<_B(fcXftfREYOT_tezyg^@6eB@cy;mKGnNVm(198W{0< z`XF$<@G0HQOjfL*Df&}Kjf}Rwe_Bqh#kT4Wj`Uctf=U&TvZew0lS7DJ&M2MBu6vvh z@1bkSyh^xh?h@Ap=+X) z4;)yPjiufp#dj?syVQ+Wi>{}8&M-^)6s5~3Y^a)L7@=KQNMB2k0`Z7`_PQDEAmn1* zqr~DO-P9<`buFI?4&B!av;qdEE&_a=RLoIstkztxT@&kfyynJ z4UX&K*>I0eNVv?*e3iA0>iqD`Oy&E_E@V8$yUxIZ@GT{!p{fdfh9-O7b-!;SOni$o zGL&w^n9qKH^TyQ2XHUVqHP_<4(tKFZIaO83coa(3O8}{D1%~&V6XL<-DFVVWPS)G4QbVJgK=ea{Ia3%UtT)=+fqby0?$7k}@P*!a zXqg3p&oc1%ylXQ)ZWH%r^DCnbbVu>&O@0_p)v8a^c+&E#zy6~2QdPRAcds`AE|@O( zB_Q_>A-4xg@4wyrWX*Inev~{b-f5_k_n7dRQygMv%h!5pYktU*sL;=g{OF0K!l8w` z>Z;{j%8RPga~D0eZQymRIzvP84Ue1OD|=HkuL*w!v2U;@0%#n8*!nu=r~de7zHa7g zEOx`g<8@mbMZ^$~N2=CK3X7KcN2r#>^s>d?YZ?2V#8s8F$czqA-RyZr-c%RX5O z=h&7w<%&x+K%b2DVI4HGKclLU)^LvTwS)>DuwR^Vqr!xs`hL6i z%V=X7ChcgZrpr<37sVU(r~L`9Ygm4^b;xLO@zJt{gD)D*BW{q0h+?8W=d=imNfi4Z0^Lv5Ksd2YDIX zi8u){VR(6cePKbtD?rKw*`HCvMsR!kC(qLF$jgi0n0)EH9NEwin5we#<45fpd5n(s z%jn3K=&~NJa58*K>(?aZ<_7jEblmOF7e^Bl=!S7ba$w8Y^R*o~hJj&9-y~8D5<_PJ z3VunfnX-!yK*b*sGVT$?=s;}9xHd=bM;!I*s|!H%gYF7_X15ptEf2}Udy8Cb6gErZ z80EU?XHOy!DvSjY1JOcolu)G}@R%r+QVn*+Pri3eux-!wV~ zAwbQ*7@2s@gAw$gS#*JlT)Jk%%i{FP=~$|q z0w#L%HfcT%eBM=-T%R?cNihr79~l2fj<)um>nl!07eKdM;mmBRb-pPm?vQR5=RBL5 zRRDU&_)_MQieaxcHO8^L`-OwZjrydiLjqKlOLxa10cuD(sDo}cTUIf;&!)AHQ*pRa zRlA=RA{Qt^wl$vY7^t98_~ntTPh_)07rRl%?|l9=G2xh+a$H7sFg*>eXWr;Iz?9;N ztj3|$(MF^pWCG860ri?bzS1W>PnEA)TSu!PDCPrGppt@M!1huw*T&lqrTvXXRlNhm z_R)oDcdzVsS4}v4OmZ&UcL%B4!4aQ$GEx5klXT%WqVp$L+J@@th?qFJY~<&f+kWWG zG69JtU#|a}m9*4}bNp@!ati=qNq~_0PS6IYU;gkSiAG)q(_Rgv+Ah<6Q7pdw&WSQ~ zLeFuS5t2oR$x4(Bo*3>?=~vu&o2E}g)?0Ipb}uz^uoJYikCMRO%Bp>&^r9`aB<*9` zOIxe4hC$ZlX9?lg$I`M*zF&Y_{s`-h$tWsA#Y!6&thhbGk!z)Xn#;QEZT>1RMQY7{ zCSNJ(i1VBeML~dE`XTi-7^?=dOiKa4hM#-$Cb6n&nQ2;Ek%=)=LDeSE9_=~F-$`Slw+el`*RU=0Y_IC2BVG8HA1gX!FVfLYcVq> zMB>HjX<~)_VZQiVFM`->lrt8gD?93%^5@TuYIjFKV{w`3)!zL3LnAMY9#LGrzBYpu zKDm;VGzUWm_Tgf*A77h|QB9HsENABGi22v340@glP?GB)F9pgCdk;q@y;9Wz_g zC;D13K*oJx^Oi_&PT5+nLeC3c_;CMm(q!}c+wX`oG*wjgv>hi#9rldm2 zHGVSG-2+ug>~^-xXrEhIq7w`4DijjNymV74%rih5Pv zcLribs{NUoG%+5%+SjW2_-@k@iB?o6RTLF!WMv0E09WT~9lz|(1N#O4bITlX^$_eP z4vl83gHHll3kb}vcP5en9q-+{Wy0h}arhShA-5_r_90CgpB*a~P;eP`VZKyRdFww} z#2~&m6fyG8}!=RTfozpCltkKTfKPkYkpcI>XIrx&uD2fpJ2V7ULwvcx=+n` zgDWWzOl!KrjM1(I(k%zwie@CqRK#DZ?J}W4`yk#wmerD`n!32rA4_T2P`IDW%ggiB zLg8#ZV9EiK_T9Vp|MHyp-eWf?!Xa1V5JF&Mo1dAry)DF?p3&6ZupLZ`#Vn+b|LwjE z0lZ<<(xS$ymsmk8vr9XdmL7jX!c+Jmdup6pdY@s(1m!(-kP^ZS*!TUKzzZA3p%bdx zSY~ej%J0f4=-|JkC4DC$?l#}-UrR%aqvjRWeuS9~o4a2PgoS;*>zjy0s#elTNy1I9Db@xRum{>bIs@4bThRj0TnHl#JWms4lrfdZo93*;L?}xvGyeY&ggKiz9xp9)gO-9uarR$O1v%Ha0 z`VS~}e9Mmg!RG|Y_$XQv*jnr@xTIHZE6}6Z&x%32Gxy3gRNgrvU%iLAk~Ic-0piUq zd&yLE$QX*_WqBb~GjhA`2K`t*{aB4zx085%_U-U^ouJQe*%eRn(uta*RWfw4H@RK) zPhkcd?I8`LVT3xAKg|f2qNy_AUiJB~7nMBu_kqyEe3qYqs1%y|pszLItYiLZrf9I7 zo4%=Ng+jgI0f(2ubuhRlk7Lolu+%7 z9Kqc_e)vllPM}2o0XpWlr?pRn*-xV)HQL<8Zkcu2+sA4dKKb9tP23e(C=|PWl&Ql= z$IjZnSqhm?&QPT*kweZc5$1_5SEi2GO(1;rXi*_SyLJe7s}1wMeBWdNx)m>!(VMdp zXJ$Je5$}&MK&Y5m4sQi4&LJqKpNCIsaf2W>SvVu{9kbimbf5+TTCwNCh9P z-Km%vS28}bfA+pUli(u}#A%#Uv};2hbE=GXw58K3#aNpP({-3~tCwJr+bA|ziR$8eQ;D3x6J z9l3uOeJbRqr#yk>zw}u9HMltSYXADr8ePv^52yzDDTRLj3USGG%}iW|u}5i~mq_f? zWG0}ObVOD{<$=)D2;{%L;Z`FF_|}JnsLG#5jB8i8;J|+ruRt(M_i(&BX`+ubHuet= zdoy1;7%#Dnp!*NhGXP61gWVbFdHP_iGQwXUH4Kh>fysk88ZZA5`#Q*E7&yGBMFF9N z#)0V-3P;SC*#CFn=ZSsJ^u?pOb|AxbsJjAaiQfNF2LY)f2dmP)a2UANNWnCgqd`vx zKcGw=>1){gA9jhvk=7Z=X9gsB`)sOn2l_b!4gd2Y!VpN$fx{X1d2K<1;1p4h+WAzQDDH&RV@Ldarni54`}`O z46uV3=O4*K)cpIf2RS`Ef?S7$?hqi8cW^HU#>Wx=&>Bz-|07Vekbd}}Z&L{kf8V(r z>O*tz?hLLg(Vo0FFICaYd9Z1peSPiUPw+2Du)DVZ@fxfO`yb#7u;d^9OUrZk4KSeo zJ@$Y7_~;AJ;DBRzBR`H literal 0 HcmV?d00001 diff --git a/documentation/uml/wasp.mdzip b/documentation/uml/wasp.mdzip index 169fb8e92cf5e7820813215a2570e1f9f5b4bf81..6ec789f5d80f6f970f8e328ad51bca1e47309ed0 100644 GIT binary patch delta 114001 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 z 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 Fe*pSWoU{M{ From ed360c2839792c5a4059c3edafe342dc304afc98 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 8 Dec 2021 10:40:29 +0200 Subject: [PATCH 003/120] Governance SC: Accept chain owner nodes immediately. --- .../vm/core/governance/governanceimpl/accessnodesImpl.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/vm/core/governance/governanceimpl/accessnodesImpl.go b/packages/vm/core/governance/governanceimpl/accessnodesImpl.go index bded42eb38..b171d60c81 100644 --- a/packages/vm/core/governance/governanceimpl/accessnodesImpl.go +++ b/packages/vm/core/governance/governanceimpl/accessnodesImpl.go @@ -58,6 +58,11 @@ func addCandidateNodeFuncHandler(ctx iscp.Sandbox) (dict.Dict, error) { accessNodeCandidates := collections.NewMap(ctx.State(), governance.VarAccessNodeCandidates) accessNodeCandidates.MustSetAt(ani.NodePubKey, ani.Bytes()) + if ctx.ChainOwnerID().Address().Equals(ctx.Request().SenderAddress()) { + accessNodes := collections.NewMap(ctx.State(), governance.VarAccessNodes) + accessNodes.MustSetAt(ani.NodePubKey, make([]byte, 0)) + } + return nil, nil } From ac3c0b31afa6aa0e8ba0d1ae7b694499df4e7028 Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 8 Dec 2021 12:40:30 +0200 Subject: [PATCH 004/120] Node identities are stored with the DKShare. --- packages/chain/committee/committee_test.go | 2 +- .../consensus/commoncoin/commoncoin_test.go | 4 +- .../commonsubset/commonsubset_test.go | 8 ++-- packages/chain/consensus/setup_test.go | 2 +- packages/dkg/proc.go | 12 ++++++ packages/tcrypto/dkshare.go | 39 +++++++++++++++++- packages/testutil/testpeers/testkeys.go | 13 ++++-- .../testpeers/testkeys_pregenerated-1-1.bin | Bin 341 -> 343 bytes .../testpeers/testkeys_pregenerated-10-7.bin | Bin 4302 -> 4322 bytes .../testkeys_pregenerated-100-67.bin | Bin 42612 -> 42812 bytes .../testpeers/testkeys_pregenerated-22-15.bin | Bin 9410 -> 9454 bytes .../testpeers/testkeys_pregenerated-31-21.bin | Bin 13241 -> 13303 bytes .../testpeers/testkeys_pregenerated-4-3.bin | Bin 1748 -> 1756 bytes .../testpeers/testkeys_pregenerated-4-4.bin | Bin 1878 -> 1886 bytes .../testpeers/testkeys_pregenerated-40-27.bin | Bin 17072 -> 17152 bytes .../testpeers/testkeys_pregenerated-70-47.bin | Bin 29842 -> 29982 bytes .../testpeers/testkeys_pregenerated_test.go | 2 + 17 files changed, 68 insertions(+), 14 deletions(-) diff --git a/packages/chain/committee/committee_test.go b/packages/chain/committee/committee_test.go index 696af24d72..400e5c4302 100644 --- a/packages/chain/committee/committee_test.go +++ b/packages/chain/committee/committee_test.go @@ -22,7 +22,7 @@ func TestCommitteeBasic(t *testing.T) { defer log.Sync() nodeCount := 4 netIDs, identities := testpeers.SetupKeys(uint16(nodeCount)) - stateAddr, dksRegistries := testpeers.SetupDkgPregenerated(t, uint16((len(netIDs)*2)/3+1), netIDs, suite) + stateAddr, dksRegistries := testpeers.SetupDkgPregenerated(t, uint16((len(netIDs)*2)/3+1), identities, suite) nodes, netCloser := testpeers.SetupNet(netIDs, identities, testutil.NewPeeringNetReliable(log), log) net0 := nodes[0] diff --git a/packages/chain/consensus/commoncoin/commoncoin_test.go b/packages/chain/consensus/commoncoin/commoncoin_test.go index 8dadf7aee7..33e9336e8e 100644 --- a/packages/chain/consensus/commoncoin/commoncoin_test.go +++ b/packages/chain/consensus/commoncoin/commoncoin_test.go @@ -16,8 +16,8 @@ import ( func TestBlsCommonCoin(t *testing.T) { var err error - netIDs, _ := testpeers.SetupKeys(10) - address, regProviders := testpeers.SetupDkgPregenerated(t, 7, netIDs, tcrypto.DefaultSuite()) + netIDs, identities := testpeers.SetupKeys(10) + address, regProviders := testpeers.SetupDkgPregenerated(t, 7, identities, tcrypto.DefaultSuite()) ccs := make([]hbbft.CommonCoin, len(netIDs)) salt := []byte{0, 1, 2, 3} diff --git a/packages/chain/consensus/commonsubset/commonsubset_test.go b/packages/chain/consensus/commonsubset/commonsubset_test.go index 53a67c4f7e..8fe66e6977 100644 --- a/packages/chain/consensus/commonsubset/commonsubset_test.go +++ b/packages/chain/consensus/commonsubset/commonsubset_test.go @@ -51,7 +51,7 @@ func testBasic(t *testing.T, peerCount, threshold uint16, allRandom bool) { ) t.Logf("Network created.") - dkAddress, dkRegistries := testpeers.SetupDkgPregenerated(t, threshold, peerNetIDs, tcrypto.DefaultSuite()) + dkAddress, dkRegistries := testpeers.SetupDkgPregenerated(t, threshold, peerIdentities, tcrypto.DefaultSuite()) dkShares := make([]*tcrypto.DKShare, len(peerNetIDs)) for i := range dkShares { dkShare, err := dkRegistries[i].LoadDKShare(dkAddress) @@ -100,7 +100,7 @@ func TestRandomized(t *testing.T) { networkProviders, networkCloser := testpeers.SetupNet(peerNetIDs, peerIdentities, netBehavior, netLogger) t.Logf("Network created.") - dkAddress, dkRegistries := testpeers.SetupDkgPregenerated(t, threshold, peerNetIDs, tcrypto.DefaultSuite()) + dkAddress, dkRegistries := testpeers.SetupDkgPregenerated(t, threshold, peerIdentities, tcrypto.DefaultSuite()) dkShares := make([]*tcrypto.DKShare, len(peerNetIDs)) for i := range dkShares { dkShare, err := dkRegistries[i].LoadDKShare(dkAddress) @@ -195,7 +195,7 @@ func testCoordinator(t *testing.T, peerCount, threshold uint16, inputLen int) { ) t.Logf("Network created.") - dkAddress, dkRegistries := testpeers.SetupDkgPregenerated(t, threshold, peerNetIDs, tcrypto.DefaultSuite()) + dkAddress, dkRegistries := testpeers.SetupDkgPregenerated(t, threshold, peerIdentities, tcrypto.DefaultSuite()) dkShares := make([]*tcrypto.DKShare, len(peerNetIDs)) for i := range dkShares { dkShare, err := dkRegistries[i].LoadDKShare(dkAddress) @@ -264,7 +264,7 @@ func testRandomizedWithCC(t *testing.T, peerCount, threshold uint16, inputLen in logs[i] = testlogger.WithLevel(log.Named(fmt.Sprintf("CSC[%02d]", i)), logger.LevelInfo, false) } - dkAddress, dkShares := testpeers.SetupDkgPregenerated(t, threshold, peerNetIDs, tcrypto.DefaultSuite()) + dkAddress, dkShares := testpeers.SetupDkgPregenerated(t, threshold, peerIdentities, tcrypto.DefaultSuite()) acsCoords := make([]*CommonSubsetCoordinator, peerCount) for i := range acsCoords { group, err := networkProviders[i].PeerGroup(peeringID, peerNetIDs) diff --git a/packages/chain/consensus/setup_test.go b/packages/chain/consensus/setup_test.go index 84448adfd2..a68de5c336 100644 --- a/packages/chain/consensus/setup_test.go +++ b/packages/chain/consensus/setup_test.go @@ -116,7 +116,7 @@ func newMockedEnv(t *testing.T, n, quorum uint16, debug, mockACS bool) (*MockedE log.Infof("running DKG and setting up mocked network..") nodeIDs, identities := testpeers.SetupKeys(n) ret.NodeIDs = nodeIDs - ret.StateAddress, ret.DKSRegistries = testpeers.SetupDkgPregenerated(t, quorum, ret.NodeIDs, tcrypto.DefaultSuite()) + ret.StateAddress, ret.DKSRegistries = testpeers.SetupDkgPregenerated(t, quorum, identities, tcrypto.DefaultSuite()) ret.NetworkProviders, ret.NetworkCloser = testpeers.SetupNet(ret.NodeIDs, identities, ret.NetworkBehaviour, log) ret.OriginatorKeyPair, ret.OriginatorAddress = ret.Ledger.NewKeyPairByIndex(0) diff --git a/packages/dkg/proc.go b/packages/dkg/proc.go index cfd19fb98d..c037ddc064 100644 --- a/packages/dkg/proc.go +++ b/packages/dkg/proc.go @@ -522,6 +522,7 @@ func (p *proc) rabinStep6R6SendReconstructCommitsMakeResp(step byte, initRecv *p make([]kyber.Point, 0), // PublicCommits []kyber.Point{keyPair.Public}, // PublicShares keyPair.Private, // PrivateShare + p.nodePubKeys(), // NodePubKeys ) if err != nil { return nil, err @@ -570,6 +571,7 @@ func (p *proc) rabinStep6R6SendReconstructCommitsMakeResp(step byte, initRecv *p distKeyShare.Commits, // PublicCommits publicShares, // PublicShares distKeyShare.PriShare().V, // PrivateShare + p.nodePubKeys(), // NodePubKeys ) if err != nil { return nil, err @@ -644,6 +646,16 @@ func (p *proc) makeInitiatorPubShareMsg(step byte) (*initiatorPubShareMsg, error }, nil } +func (p *proc) nodePubKeys() []*ed25519.PublicKey { + allNodes := p.netGroup.AllNodes() + nodeCount := len(allNodes) + pubKeys := make([]*ed25519.PublicKey, nodeCount) + for i := 0; i < nodeCount; i++ { + pubKeys[i] = allNodes[uint16(i)].PubKey() + } + return pubKeys +} + type procStep struct { step byte startCh <-chan map[uint16]*peering.PeerMessageData // Gives a signal to start the current step. diff --git a/packages/tcrypto/dkshare.go b/packages/tcrypto/dkshare.go index 686e3cd914..5ce9042255 100644 --- a/packages/tcrypto/dkshare.go +++ b/packages/tcrypto/dkshare.go @@ -11,6 +11,7 @@ import ( "github.com/iotaledger/goshimmer/packages/ledgerstate" "github.com/iotaledger/hive.go/crypto/bls" + "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/wasp/packages/util" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/share" @@ -29,6 +30,7 @@ type DKShare struct { PublicCommits []kyber.Point PublicShares []kyber.Point PrivateShare kyber.Scalar + NodePubKeys []*ed25519.PublicKey suite Suite // Transient, only needed for un-marshaling. } @@ -41,6 +43,7 @@ func NewDKShare( publicCommits []kyber.Point, publicShares []kyber.Point, privateShare kyber.Scalar, + nodePubKeys []*ed25519.PublicKey, ) (*DKShare, error) { var err error // @@ -61,6 +64,7 @@ func NewDKShare( PublicCommits: publicCommits, PublicShares: publicShares, PrivateShare: privateShare, + NodePubKeys: nodePubKeys, // NOTE: suite is not stored here. } return &dkShare, nil @@ -120,7 +124,18 @@ func (s *DKShare) Write(w io.Writer) error { return err } } - return util.WriteMarshaled(w, s.PrivateShare) + if err = util.WriteMarshaled(w, s.PrivateShare); err != nil { + return err + } + if err = util.WriteUint16(w, uint16(len(s.NodePubKeys))); err != nil { + return err + } + for _, nodePubKey := range s.NodePubKeys { + if err = util.WriteBytes16(w, nodePubKey[:]); err != nil { + return err + } + } + return nil } //nolint:gocritic @@ -176,7 +191,27 @@ func (s *DKShare) Read(r io.Reader) error { // // Private share. s.PrivateShare = s.suite.G2().Scalar() - return util.ReadMarshaled(r, s.PrivateShare) + if err = util.ReadMarshaled(r, s.PrivateShare); err != nil { + return err + } + // + // NodePubKeys + if err = util.ReadUint16(r, &arrLen); err != nil { + return err + } + s.NodePubKeys = make([]*ed25519.PublicKey, arrLen) + for i := range s.NodePubKeys { + var nodePubKeyBin []byte + var nodePubKey ed25519.PublicKey + if nodePubKeyBin, err = util.ReadBytes16(r); err != nil { + return err + } + if nodePubKey, _, err = ed25519.PublicKeyFromBytes(nodePubKeyBin); err != nil { + return err + } + s.NodePubKeys[i] = &nodePubKey + } + return nil } // SignShare signs the data with the own key share. diff --git a/packages/testutil/testpeers/testkeys.go b/packages/testutil/testpeers/testkeys.go index 240c1b6fdc..dddd62e1e9 100644 --- a/packages/testutil/testpeers/testkeys.go +++ b/packages/testutil/testpeers/testkeys.go @@ -83,15 +83,20 @@ func SetupDkg( func SetupDkgPregenerated( t *testing.T, threshold uint16, - peerNetIDs []string, + identities []*ed25519.KeyPair, suite tcrypto.Suite, ) (ledgerstate.Address, []registry.DKShareRegistryProvider) { var err error - var serializedDks [][]byte = pregeneratedDksRead(uint16(len(peerNetIDs)), threshold) + var serializedDks [][]byte = pregeneratedDksRead(uint16(len(identities)), threshold) + nodePubKeys := make([]*ed25519.PublicKey, len(identities)) + for i := range nodePubKeys { + nodePubKeys[i] = &identities[i].PublicKey + } dks := make([]*tcrypto.DKShare, len(serializedDks)) - registries := make([]registry.DKShareRegistryProvider, len(peerNetIDs)) + registries := make([]registry.DKShareRegistryProvider, len(identities)) for i := range dks { dks[i], err = tcrypto.DKShareFromBytes(serializedDks[i], suite) + dks[i].NodePubKeys = nodePubKeys if i > 0 { // It was removed to decrease the serialized size. dks[i].PublicCommits = dks[0].PublicCommits @@ -101,7 +106,7 @@ func SetupDkgPregenerated( registries[i] = testutil.NewDkgRegistryProvider(suite) require.Nil(t, registries[i].SaveDKShare(dks[i])) } - require.Equal(t, dks[0].N, uint16(len(peerNetIDs)), "dks was pregenerated for different node count (N=%v)", dks[0].N) + require.Equal(t, dks[0].N, uint16(len(identities)), "dks was pregenerated for different node count (N=%v)", dks[0].N) require.Equal(t, dks[0].T, threshold, "dks was pregenerated for different threshold (T=%v)", dks[0].T) return dks[0].Address, registries } diff --git a/packages/testutil/testpeers/testkeys_pregenerated-1-1.bin b/packages/testutil/testpeers/testkeys_pregenerated-1-1.bin index 25d86fbd331f9a73819adead4b866e5ff6729309..e847dbd39ba049cd514cd1f6eb5a6769cde5ce2b 100644 GIT binary patch literal 343 zcmZQ%2xep~-`AOV=T^s5rWKWW4(rZ!q{T&E*}w1PTbm%B#a*kfK44&A1i}UewrTTs zF#P{NxwOb@%h8#iUTKRi|N1jGHY=Uez2so>EKeQB)wO?i;l)d!)i`8?=n`X4LG5>X!VNISquz}K-j<#u|>aX z>NfXM4?)(2JRcG&^~DNb?wn;-;o8z`bNtlxsvrx^4dowiX6v>3d&tCh|2W!K`(@sI zbGiK!V&t6<2whfxv{EfhZnCg{hbM>nBY*$A<;S}C=d|ou?foxI`?JDcg$e5wm)pze zdYsh>-(QeCrL0uqR^|atO}S@!zV;33ZXkCIyekwK#M9?p@UV7!zn=ZD&HQgpUrU|k SB%<0l%Q~L_pXL(gvlakSwvhe+ diff --git a/packages/testutil/testpeers/testkeys_pregenerated-10-7.bin b/packages/testutil/testpeers/testkeys_pregenerated-10-7.bin index 633fcf96618ff5dda470dd8c879cecd0f695be23..e7ac38cb68032246f41d06abdec35db31fbe1e91 100644 GIT binary patch literal 4322 zcmd7U_dnH-7YFcnu04|(*G2Z;B2iYZ?0XaHmaMvF7nvFN;<3dt_#Zu30p2BShTH zdx^5U=Ev$r`t~1u{pkD!=lOWN&Uw6;0e_}54Sbv+dByIhv7)#9SUR@w(2L;_+(e(D z(fqpY!C96)0AL0f05pK&(5{u8_j||wepv5W6}(zwP(uE$Iec!kuvRE-L$QUH_6Vy_ zwK|XCl@!34n!ae8L6RdqGz;S1ZnEifHWBE?)}-e*&cfXcg|x@y8vNq&*8A)NH9C^3 z-rP@`V=p`pk>>lB7WW}?TjI9wYY1jdT)I%}qts9G?i8Pt)-Tj)*Z~b_Ujk6(w|YG( zjgYQ&NaIx1Hx(P{MfY4=`&pS=@9o>%OFCU#@y~cTyayWm$es3yyo0wj=lQn&0~_tv zMeWpSE++Y%8~@uN6!xuBIa5^oA<$yM_#Ep;8mcFj2lXDIZ&=Cdx0K}hK14e`k6z|g z`@udoz1(8m7S0dGiYTUE;H4g~R%TOq1B0?1pR#zZvzX%u(_a!eE>67s9bz`3S*u-w z2-%&Iy4FBT@vWEcUu7nqQ9)#Mv1_y(!=is2sc+9Ki<^bLNl+6DL<4bcxq_el%}jRO zdd3Ys53-J{9KyY|V5lpOd4`Y?E=LR!`{pQPeZD#Tb-pek@cPpZZL#Mf1J_H88-{la ze&~l^d`^vsH?Hr8FLM9p#FrDiYvLvA@13njLvbbSZ*y;y9$3y2rXc85-F}x2mWJ z)ZKIqgCe4|=CTe09#jZ& z2)M7>L5Z!Lp1P(QMo~dpj@s3*OAV+Ow_JSlU#XXrE5%$@w}G_PqSbbOZU`%Y^&wNz z+X`mmWFMxbE@=Nj1ED4fted1S--aP@j}nu%--YtNkJ_5c3MXD+edd8jg)qr&6snuI z>>`Ijx7Rf~NG~-PuBk4_)KaL>cryvU5YUxb8Diz|$W-=+ZP!a71J^;F+k%|d!Ykt9 zO}QhO6volC#Hjx&3UXWbE&Y`tF|v<2HW}=%5`t10mXiIcMCN}#fob4XJ`%EC*Nae&DvV7*T^Rac(yR+BU1uwJU&H4wu%hD^K%6pgn?yvXHbCC>Q8K0f=E)sYYjRu^DrQrFaMmkMhvXlJJhj(-5;6#{B{3e0vCSsWN8uHDO?@wa97)-5)DKk>^5$lQ!B{KhB|!v#BTMXhFUYT)-1-&3h!(}vR>qa(~VxsP%4*| z?k5~pjBE*>%apz8D#>|ng_F$HlR>s4APFRgsM%fgL3+)6^rk zCWB`Dc2HG0pQbVh+~;@rLks4%O># zl^{L@3t1qVLrIKm`J@n*^Bb9W4YS0pf1&*>ZdZOydM^suLXLK_ zE3F=5F-zmc^lOW>kB0RtQOB44_spK)HpFlTc!3=>pt6!al-3w%DruS`GPPQIN41B( z+Ky|jImXHd`i^FAGRuug%n}7r4KRx76XL|WI+XKHWyX)SbB2wcsuVnpa88-LT z6RX4#Z;NQao;(-V4`phEfz`yF;E2bTRKxe~rDA4A#r900n#sOX&jLiLxBtwMVfoDW z`jQc^Y5v_pkV5t|Hk+ZN0cQuZt|dd-WxTD07!+j0Smc}neo@6vQ8s8cu0`Q>om{i|-6y&?y&D&uj(U|Qd$X(~J@dXNuph^=t5^h)6t!IX zjDva9tA(AQg7(pS4>I#bIu90aPh8Nm&4@tIOX5TTOYQ^Diof};x8=)>Lin}X$F+}p zLThLT9V6Y25;1!_05Agl-_mvFl$0*u--rNU-5GH0aiYW21J`{lx+&vMP*`xmo&YRt=oCZlvuM4~-aB&fqzqYcHzXq)6VC#I}s zU9O$V#*FG=U5$Y&6fnrr`9vhTQ$@PUIS58Dtz?-X7}|YWnm+K%U1eaMg$!}!#`M1QJCGMcm=mU*=MUr77|7#_8}fRVbeUMB;Gs5y&J;)9PjPefukRV4VB>h9LP z8|D< zOA+`mdu3&s2&=O?5sB$kk=*Q>bE%}4VirZla?n&?1x?gi2-io?bea_WC4^LnH30k% D=h^C8 literal 4302 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 DMLosO diff --git a/packages/testutil/testpeers/testkeys_pregenerated-100-67.bin b/packages/testutil/testpeers/testkeys_pregenerated-100-67.bin index a7953a510f4797294b29fc038e88c5e224eb93d3..8a50d5c13d1d6335c9a2a17332635a2b577a0df5 100644 GIT binary patch literal 42812 zcmd3tgLWoNvxVQ-wrx8Tn-kl~#J2Uuwr$&XzSzmcwlT5)oVz%8(5qH=?OnT`HUZ?R zf!Iw*t1dER6MQJ>4^reHp4Keoia#XShp7K5l+YDOCS=otVA}e#8=NaRgF6@a%7N2MEz zL3?tBpaZ?qb05QR9M3N|hFH(MVj~s!izYVHudX7282!P$IlB50PY(_RaNjy#EGe-f zN}7YOlcDuyw(P@ACUQY~7Db445K|1b_yFPBvtMJl{2M(BxcM4N|E5igPjcUgF1eZm ztD+?>#0YV{JP1~ZXoC5CO{;tB-h-h9wa7KI-!3XEkIvnJ5;F4trqp@|oUl_I&KB{V zsCuV*92U!fkc8%?UOX-PKtLFC*MT}goL!MNf7|IQvqdE549bl|ClU&~QqI{a89;hn zSy(!XVkU-(V-i{Kay>Ig!dLuUjz@0 zL>`>UG-Jz0F%pwDJtm-BbIxndpty*uE z%rd=3t4z&cxx6LEu7p!X5`c2gh2D~^riT%X8;6BxTqj=4X7ehcz-Ps}Yb+i3d|sM9 zE2zK0O5Cpd{EmwdQzUR8D={}|=v_nN-AD9LbrsQN^N&O%xKe+W+?c9APzAyeGGM$| zO{H`znr#kmZGq6vDgTli2mn^?W;T?adrkZXcV-(Z`UM`g$#5w#vaCTwQp56e8?BH? zwHh*1{T9RLQ8#w6zq@$D5(wC&GBG^LGkAqC+5{oz+g3|7Kt|sT7*9tNN&M{uVI=Qr zmK=_t3ig$gT@x4c#O=252u0wRn>@bgTmRjwpY^i){4L~jg`QQ9`0OX1B~yJ2n1SW&lE z&j@$(0?14g3RE0o!xCKt+lDwI!&HU5#41z#%RnJ>l!_wImlu?#bgi9g2ZWvE?) zG{Z-@ScFUynW8G_Y9RRq&Y=RMLraxQw$boR+_Jtx@|P}EB~&4-oPhurQuM|r&G6i5 z^3*8Zq_23t-E_U+mkHv3uMt~s`5rU+J@c_WLPQ~O>`h?_mSDnk?tjkHFejQZTg8%f zcp0tu>vbD4#1IiBz6Eb{PXlK2Y~qO*y-pIQX(y&&aP47m?iqAXK{LdqAbjk~#oq<4 z2%^r;Gn-a3M@>=_G7eRM089`~$!g-!2yt|4NlT|jR!2Ivp<7qU9J5F=DBZ~=62Ha| z|2;x$ps2=T!uL(dEBK55Fzm0&I=dNVRV`6k_8+aeh9Q_vduuU8kM zLY-H-By^fxMfgVfT%`nr%i}sUCI;-xT8&2-uw2wIZd9Y@d=``386ZHe05*^uTjQ^{ zI01>ba*A*crnERjGE+0r6dQa&xNf#UE59J}Q*EkDQ&Sxc#uFN1;-2^!!McbaC$?kR z`yuS_Uc}B*L9aNz3cfX@8Jw=iO#t)0`^V z8){+874$#vY-Y%TDaWe_)$52T8P5M!lx+-kLY(*Xgz&n%3};M|HcM20r+UTc^BYv` zd{0!`y;zH!iK_{9_+M3W*fith%)dxweItD|7hvsoZH$XinB7`za{Y8=jYU=QV8(;F zWj&wuUzl%;d2EIJG~xrBX8qSp93ZV`Dj$K_?o}<)P;{eJv&2QQ1=+%1YooXlNy-n` zZZm$|Y@DsSi>bd$!(y=KG{F=g4#hRqSh!U!8#v9WnAsJAYg{)7BS&5RXr zq=-|Z>pu_r9mkeY#>_wf1o-1VL4MC_lJGi?B5~pNjN78gKi4z;i>=>pU@`yDde)=o z8o!!Yb_th8pr*Mihdesrgu=E>h7U7GQ{SmB$K+~^MHXGeMcO`Zck9^?+t;9Q9YdTh z%O^v~W>i~dB;wp&CR1i{_`Y_cp~7xs?qzzYDJb3PfV?JAaFtIHcn1Q+&Qk?lc?$-& z>Ra&xNn~KMAl%VI2I(sAJvk?0D&qQtZwhK*`i`H~sr+izjnMZd_KmgZ*YagO#6=b- zL~}1#Ao88DbY@E9#(slX@XJ{e_HQqW!W*I=I=AZtX?an&vj_tK4jaGWK8Oe~D^Rv4 z+7hs8ia^kuiIff%_gUJ304zb9#yIV|k7(dM=P`5D9?~>6=cbcy$X}XyP8JN7HE27U zxQn+<9MDT~D=nqzt(L#8Rns{gyMvkhiq#a@I5J4{l<6Y#;+oTibzDlpWG5cwZraL#nR#r`oU}*lVpZs8` zYO+)4uz@tWY4Ut*2}3CDLh`#J#lv6*CEzI_d)xt2Vr1Tih{!IvnsW>fd2Pl!AJ?oa zi>#>~Mz5}45eWP9zlT6*m=I|^|TyF#Koaom<<4dd>Wh~N5oh>7=gv#Ke2(q*m>d0YU2<;H2 zP`*m`d7%L`GDykz|9rQB0GK2dpwo*Y^t0wOnADSEQjvMG8D6D^bzH6z;Wu&IJWkh( zoY~l*KsLmI|Bdk5n)mS0Z6Hj4lbapsncVlUCcg#sppEh?vK-iG_sLl+@^#x?5b_I zQ^S19A{<-$&}jYn$y=pc&igPcgYhBwH-ZoT4189Xo0GKHHhpNH&iA*NT>ugCJMCxg zrxT)8xMg!_M}s5=i`8CzYEoDFn_a-sX7WF%Ky)!4vR6x;m=fL+Eg--PT0RZw1fhV} z{2tLRc6=Uho4Tb5CYmR%`cITa0fvId{!)VWOIU<;dMn5fx1H=8&c|-=AFx}N6p?1R z45R7@dn`uo(}k+xt7A^*)fsaD1A6^pPEnHC1*v9FV=SEOv@7UQO(&ZDDbuO;*oM3_ zHG|r047D}lyuOK*sFd)}J;2K->q#B>q|yi?)5F9~#EljcDzHA7IjL>!iHc`nWS@lr zspr90sHA=5eZDx{zl1sS$0eG#u^IxQb__JPb0PGvDA4&uviI;+~nh~)0p{o_y)c4_*#`uZql znKd?p-|dUR`0*uXXSANoqW+^^5v^6fv?@#9dV@!ACWX@PX`O#))71$?%Egg$g07?X1NLbAl~XBIkMc8gUFnCnc71;d>BQxGt|wE-Ac;*@ zoQv>(_cekQIt#DO-k}Eqq^CMwZtdB(unaB{<)!jCvjxu4(XPg*6jlCd-S8S7Ti33_~dd z_ATw(pCnv|Oqi0=V=$Lb?P{m!ATv^#IRCf~ghSkEM^fB-~%*mv>C1oPSD zUkEDe9Gu}bSlO2{_JPUb!Gs8dzU+dl5+oyu7nOcrkQgOQEIT?WR;{|9oXJzeHE0T; zT;?J-u`#;?mG%*!@}{wt$Pr?7iX>L-NU>yGe?BDgz0C(!==;5=UtMmf|M z80qinppM^y+f<%Sq5x9zIc@~bNrS3wl|Wh9RbUeZ851;wpSS04w40>+;{O&2-9Rv{ zlN|%TejYN%un#TBc};N`4tqzMHfzB2WR;<{7$=R@?F916Hc-65M<8}ApCxy7n?>(1 z#6$;jO!|wk1B-LoJ?!TuY6pu=BBvU6sjWN6wp(BW=HmWd)PW4JJddLoiYwHxz{}I^ z4(J#VAD#Qtz!wjrE6UB=pp>|KVrl%O5@w2}fwjri9q6AtdcR z?;D@hc3!Y@>UvD19c34M-OPO2UeAUjuG_!~-}`D6{iQt-nH+cr1PEn(|0{Gf#eqLW zS}jJvhb?X>uq2??;X#NhQx!T^*j7(T44z}R5Ow8?>XT=Y?eqvXAEN786--%JI-#kT zQsj9wc}5tt`NK|Vx1F&Y<#}Mx>}+ZSX%F8P2Gvv}S}G95BX8Kg^MRY0&Pjp{)E;Q@ zAkL!ex5-}u1RL+vVgUgpWlj-Ny3_#(2(=4}I#A*ji+=%mctSmxmd30!(%FW2AD7Rz>v;l>EaEXHAAsI<;zz* zRY@5c`=U?1m2_6V0{)Z3T(R5W#tQ6r2!#OD)iYyOS0*%%&H1KJZG{mg}hAmJQG5T8gTHaVRYu`4M3^@3Y>rOb&&HqBz! z%X9^f@uVkVO*Y{}0#fsg)>U~hVa+6=%E51SG!FUIbZL{VaTQ}AfRoZ}rY->0g=4Z) z${u?OHeC%wcW%OqLQL{Y6b(RBsl~BIks{vlC1Mlnwz$$IN-Ia8e+SYom0 z#%Ey-9V2H@kk>-`aqG4W<}a(zJB&LD*#v(WC^D&@l$WGbM%hZbI)LP%)vCTM9lCj@ z{rjDA!gh<4YHQ(ZI|w1deXr}HfBAUQ`k*D{_-WWxe^t>oCD3RP<5hc`|NUd}TKIyE z5k(MD(AgdpnL+#E{T``Qq8|NiXjE+3gcXW) zM0ncZTGWOxW`ur4+3M7IQCkqOk4T+EtO7~()kC1*p%dvrn3&u93nT-i>w)AM8goE> z0KN$nddFY8*U|)Aqj>x9QrxlbGA|AgEjRf65Fpx#HLutYh2RJTuncw01+7^&HGwiX zr~HTSEI%iMbNoDS50!~(0$!p5qRlJ&&{@;o^|qE;hYpO}S;}yd_AEXNo>Zs!`t88x zjwZx^AQDa0JMQZlr?pMU=5LD)+pOT|U1xc3lg~{$j^QP*t$6$`zU8>5=z>zi2=OW) zxcf=DkZ{tV;21-54+Nm&o&IBsz?cKO?3OAQh!}2HJRu;nm-3_&gqD1ud;$7Fnw(Py zVA`Y&ep>w7AT-6MfTF&Sg6q#Q>^GSR^=(kIyKEPxRS0qCsgm+l=-v0|YC)mB!Ssqj zwM4*z*H7oo@gD}8b`WJ_ZkWPDG=b43Rdoal>rEpA$>!dz4O;tgD7+GXI)1C2&%Gj} z;x)l*_Q#rV`LKX#p~Aam1mOApA;IV%PBdF@n93b9t0Aucjk=mZYAaaWlbY4^06jt^ zDy$yZ+vobv+oxxlnv87^v@h=6_4f1(J5NMRXh=i7)-bv7cPHx^FQ3pb={w%Yny|V} zKCo{*mTZpi;JO>`N15cCxYXjI8%Uk>yx%Whf`Xy(HOwU~d$`n3)=TsZ`GtQ1j_AhP zrx|G@tS93+0;p}N6_%g{xP#Zz5$zr~3SFfr@oEYT_OJO*gjzzq%jPQ+^%N@2FGP_d zt9KrpH&|^2+HHG5ON9XY8j}|B#+5hzd`dk5UcH|F#mmQacU&OAvQcI;rdY+tmaDOM>^u-Ims> zDR56{9eGYZe5)S)4$79g!AEk%DHkcrD}|qx!f(u1aG_{sE!n?iFGipBUxbm!r?V^E z8LfU(yc2jYV1z~WqWG{OqHCay_S=uM$R0#uwC~P@4@@<%p2Hb@=Y^>C40vRZH9>1# z(AL14I2j|&%%ng4q&O*$>Kf4z4C|76|D5=+T3z)bM|AS9FyNTy8+~La=o)mNO zM#Vz=<22_`(#OLgoZN;n<`S@FP`+(uNBt*@T>+ig!le{!I{W~4cIK|DKTa*OR^cDJ zmoM3fv`aK(GozHC&ZgS7alK?G1Y7+((U&ognnvyz?e5RO90#R() zFGs})?z2U=TZ=Vk8fRDQQS@pD!OtZwIMfl+2UdFT_dArsU;@mM`C9r32Y$P+Up*(R}vr?c)QpAV8Ms zaJ4VGH3lp*)!A9@XL3ENlf?1oR5&%heHKf721n^ylR;8}KosLrlhY^9wDN_GCltxF z8GM?O-3k?r(UaU9X_wCR49n275q_mL}KLIDA;vuBR;i7%y~x;-PN zq~@YFY`DSpGsxMasC#^PY!fgrB03d+5l6eyo*v{O?1Rdl?jRcyMp8*Kv)~d^i_DB5 z)@}c=on?%faY=XTf;|Ku4_&fNdbk9g(Hs7LUEU|RMaR#E7OQ)tdtvcwgP&A;%rg^! zCmP8aFISN12x)l*0^q;tj+{wY6ed+44^&0WZXNl;n^>|a!xR8K9z@M5-cfZ3o>=Ly zbA>A|-etA<^$*HA{Vp|(Drr5q9>Dg*9Q{Wk-Ec_X>&ant+AR2tQyh0=jasM|z3W)- zJXMc1NOSQ%C52KNI*#C(@5+@uBZeeIwUzl^pmn0468tMK3J{=6Gy0owqls9hv=8Bm zGsf_s@c@vc4kp8U2p{A!YR0X`%K3_fa3tgz$!#(Frqnq&Gb|NbPM$S1a!f}J3v5O0 zi-|19zz-}J|7UPD0~`adeaCwSN5ypqdt99X95!p}5F1mS(nO3jV;I`Kz5zOqE-Sm` zWLcwN-6A%2>;!(M_ZaULT;}868Sc8L=S37tS~|A)*fIiJTN1o_J&%Q1z0pqQv-!6g zv53hCQQ5l#Yjbc=teN)Wh({xurm)zfRmH}Y zagefAsvQM#$)vXT#~K<;IL6V~zCilzUkuH@P&=V&25P>?~%Mq8|bu~MDm7eFc1tEgS8@{2#Lff)_-Xkso ztx1bT;7 zjkGVpf)5zH_@hj=_?VZJBm^9{FUcNNiTc6m9iUJ;Ia#8&yi$3k=f7 zIanmFN?7nWwKJQJWfu}?8~1o@hTt#M-ttF!*XAzPr_+!7Db>hyJcV_fdW?{KpEZXS4T6n5jJ&=xd7uU#ah+bI0GM6(vp~zmt%NNrumkEB z7wI_^cS)Yd>W?xN&{x#RhWLO4{HO8Xl2{L4d5pMLLkBKPOccbrQuTr5NU4hi8u zSZi={My#A642$|qfP#_p&O8GcE~c%J!Qx}nx+nKo`vz7HfNif5n+Xr(ixga^LWS20 zlLlDyQ;9j(^c2iQcn&-}NF3pU@6GMUuh+iyOWc~7>px;mcX z@NKwiX$HDm@*>c57sfLq#>((kGJsqT8oY7=WcStRfrI2p5LUu?E0?_)8d{Ofu7 z+g67bC7qhn4hX={hN{#=?~V7qBrTWVv5bV=dyxFZFx?YN$o1unLS?lqQNe_ZV>dBW z`GSis2uhPyHovf3EM4YIpJY4WQwGHhIl3(JL_G`}9&6baC>5oQ$1p=W8~s?w%uKxR zXOj=S9C?Ur&>QAPBqs&^OO;&W)|b+grYN(PN*g6pEc?@yQQgA=xiT1E1PkV}y0{ZA zq&R(J*a(ab`-?%)T!74ChB@Iv3Ngx;h+ zE<#ux+e|pKF-83h@QGqTh2hgb)u+sXoQtS7k)Gh~#We92B=gcr$3g6!P(F%*KIIi9=!TWP{78E{;3sh-u+arb}Ioj zgKa+{N;Y|+2JPH}pFMcXu$BTmQJK(^+&3s-sxpclF8l2)>A8Olj_JK^DB1vO>Q zgHV!{)NGv7Za<&sR0K;VFh)3hl2KkN3CxmE+vs;zYz)|9rL2nlx&;E~G@{v<`!kI0(2v(!Q=n0JGs&^OTR~I6ZZDtb+dg9-dwjj+b>N;qEzQafArT z+>mT?MFRPgW2s~|>Y;SLmQ*cyVFh#fLW^v?E9IK{gCYs-Gjjx7uxqTDAMs7r5eiHM$w#IrCO1flEM3*zju(ah^osZu5?Z0$T=8Q^UpB*SkN@<w*4+28ig9v4-i<^mJ+SGs|_QrnCFzU+9Q5#tG{=&1X=SqG{a@qCDjg6js9 zJ2Tt0mUHX+qpXPi|D+d$qAw*0%E8yi1m&$jJ|dleap1xr6~P`zcJ zkSb3_*BrG?16pMH9l1S{=s!hUhTtaY0B?26{IAq!Tf z5wNg{`$B&{VW9Ei9s#YU*Js}FA^waoeI$f zUdI^9mv=Gpd-;*>tg&vmX&bwQTsvBr7cdz1F-V??G+FPF?3cOMk z(L|kTckR6y7ij_hZgb@*zZfL%&6wQL?O~IvVa8-CP8e&ezK3Z*^LAXCor2~#sC4Vu zWhG3meDX=#R49OJj!>x(9`0BlKD|Tqd0*NXN7_NC@VEc{`*BIYZo7ZZh0byTQ?Xl@ z(^|2zrSbOkmG60U+E4s;CCS)PEFDawJwc(cysd61F%fg|U{ob0#M&TEZtkTk%v0!= zJ{zT&iBmm2Vj9BN7?8#PG|9YYZ&T5kCNT-^*|5T7j-!u}buKV2jl*X;&|Pt%`s+iM z^HHmG=LFe+lryjQMVc*csbt~3fURh`{(pr!FOOO6W#qxZ}<=+}!=@~#3M zJ&MgDbY)k5l1?`xaj*ULR8@iLC`^We$z(RApD*bac&ghRK=oT>+w4@68Njw}x;Ov$5PAKps286sQJ2aEAL6L=@UDXUO2F6gYil0n^|~Ip z31^dSqv(CC^PNE?jGtsZeRmj-nSBS1jNC1?BmRfCB|&8-mDmN0AuEY!Z&Oxx5|L1g z;PEPut8IJ|a7K1yA&f`g4Y4YEUDC(c{Q@1g3_jKmhI>2xslhvO$TeNCL1_&`DN`Jm zZkB1-Cm0*V#hpDK7f;LW1)h{!*v%yTAMGuC{MRw|=as9QCdL;Ur>mDELCJ`Yaf{Tr@-RVkS{Tk+3%}?0JUfVaVg`s!q$~WR&Vd zx2Wr4v><^^Y$~!2>{H)F;%&~6zOaKbGknKoptv7HGe(6h@1Z_ycKCM!j~#wDJL)W^ z+x&_xjq8xHGY}xEu$lbi>r;|&0lsykg#{<=uYI&-`*yoslMYK4JvxTcT$g*=BP$or z&lo3~lZBXT#z_HMMDzRmNRG0{i;@ZQaL+L+lqV z_&ybsuIhMT!&aZqM`EsCfPgCP+2LRUq=A8I2kT__!?DzlUq6UhRroI-`jt4%vAUO? zGkX(hSUF>Qp#)5bK2CvpIN~tsJzc`&1ZV_u2(5r^>rAjy=05nKVe!^UM-ctr9A;re z>R^sjNFYe8D?NU5dao;a%eI`x+d;6ZyjMNMl!eEg{~LaP$~9ABHRvBK?|Sg-=ik_e zU9M9pxk@hhKQ-V&Y9NFnKpw6rK&y=z7SD?a^xM#9Cg!efnf#rkJ>BYm%F`+SXPxQD zF`!v1r_(!w{3{2q3cBqcB6^Keimiu0jQsV?*+DCJ1R|@>^<^sH5pqy^?3;uRxxpCf zkyG8o3DJwj&}8l!HixXv$eBE7YSE1I%{5MGBqjK7TzxAL0CEE_nzFK5*U;-7tWuLy zE`L+in4><$SXCS(r~mA9E&!EB4gWj6ZiB~z>2E6(B%+Mz;;S(p5TDPrEHZKZj=2DEJzE;tbLZv+;x7mx(5)-mHSZ>P-B%Uu!1SWz7SbQL3>sQUMl1Fe8TTRBW(t8w zz`gu;*UOo)x#KgJN)y+C*WQMq>Q9g-T9#JC3|BzA_qw1re#8$2Ln?JV_UNFS=ynO# zF!W8O9F$TLMk2L0luJk3AUK&BQ6N`5(*iO%=>6H^Gdg@(AZwkx!;kZI)}S4>Nd#L4_9Z?Wf({Ku9Em8u%39 z>!cXaXR(Vdq~AP$vCzcn zXq$8Z`cb009uVM8T&e?L4-y8xg&CNFo02?Pt657Kwt+a0EwcTWEL;j{ko8-u4heeE z6+{yqK5XYNhaRXI8HBH!Wujxlp|h~i5zg`z3}o)g9W%2P+8<-}NX(HGqQ33aKTh3m zer3!8m^LUBfoPdEjC$LYcSZ6QDBw8!56oo{Y3P58XHH*!wyvnyC0nr(Z@H7*y|Rd* zeW|zphkv);Q7V@gGFNmyj6@c*h*H>cXP5LiQ0r-N@h~=<4!n0W8xvs@f2~Z#R5K7b zunb&eXK>PU@-7vj91D;$So0EGkVqi4oQ2i%-UofP+cHI#4p@p0Tmw1FrLj0o@#fdwG+{9;O(0Mq#3uAdkSCHZ_96q8H z*0Cq3-J^^Ill&q>NbCgvbmD44RhE?TU$1)CaFciZX|z;8fULiaE=7Xtpw#>sAOC3D ze_?a- zS?Y(8pm77EXn_DGjc@Nga(RtJ+ExWP^SA@Q@;!9Zx}YysvNog6?HExLgj?LVSsU21 z{l-$#Xg+e7F~z=KX)yqFQMKy2AsiJcX5Ki&LNC=R_64d*qLQ z{^P@klMeseJ{@MoV0>mRIg72~avve{KAx9?4hbw6{Dad11Q_st19Zol3#Ud^J>M8J z3avXtTb+oli><)~4$dsu{+P4f=*lEvy_@}LJrOz1qpCS8xd3pa zWy1A-EBdmrz?iv>fra9D@P*qgUUq|HcSxFxH_jS@)p8Nx@gdrU5SE*vcCd5i(%%!D z^W>d29OQSv2k2}i$Pbvm&ZSakq~pwKbc5Rz1g3?hwEe*Z0w7M|5+Q@pOHvd%hpv^1 zNjFt3ktl$#elTfVm98u~jXJ74!W@$hZDvL%Bj@E;8$8L4uZNG1C% zQE+4RR_qr1WlKV{}Vo5`!^*)*=rz<<9lM0XMIix1@D>X z1-=X%DIjsTDcN3Iz>CeL%JXX1&|R?vpCeqEJvtA^-&OZ#$MGvp4f&)HG}LTb#k4MS z-q{9m#2;T%N*&w0G*LuW!(yO#jN8Sf+~@2s#xZ6W;{`0)QX0gz#Na%LKUG3~x&LX#g9(i2I4Ne$}!z!~1AEf0!Hu!ey<5FuXMVTseZCc(J)oX8(G{c%m zfk=r-LmOPFB_IHqNo!c!HFL;`@ky-)5BMnpJ*)DNU<9~>^#)RQhUoGRUipiqh#Ee#M%?B-@ewy3rXij~4{O6UT`ON-I87T^zL0jrDT5!#Abz#hoI6-{VgO$z7z30W zyWm4<{rku!OXBvQM@qU@>;q&eX7NmsH7~W5=iWB#jEj_ z9wfY9s6K{iaT!_xRq#eS+EID>O|s8JCOT1UE|>w`DW841q%N*Ap>GNaos|@9m*14+ zM}LgkD04a|aVf>IOTt!hUykX9Y#nfr_}?>O@m(ppb^K4dP8Br%l#C(YG8pX?o~~Uu z0E&w2&2Q3n^#lTNJ2yIfysH;VNk6zt!K*U8{awyBV+Ituy$emjgF~BXQRGSbt-ESz$BbPUDnWSi^zX9X7TM z`G(CyPL+aroVt`|LE&Aax?=lMtZVph98JlhqqT>kpFnQ+|A=K?H;f1328%l#786so zq=Od)6yzCnEeFZ5)W_Xyc%!Gt5@v*%gHJ0Xc*c9%>yfJD$~Bz&o1M|?DgXWfEUebE zyACN_l|9Rf1i}a`tM8q+hPd4Bzf(Bwf1R>u&!xW0rN^c_4DkZ2?l(FjQgo^|1)nYr zZ@76pAO$gvFMt4Ikt7AiJ{T&$QUhj1oX1Sacvg9iT>{iHBu+WRJ{RL(1tv%HBIN8^j2tZF#>1_0-nPa*KArS}+kH{FG66 zhBLKJU@26xYJU2`ZLgw5$>FtJ+{(tbYpWPoy{2M$9yA=6@@UKLDELz74PcUexw3#RAr zOIvO?pQnGKr@^wEf}FrR z#WJ|LiR!w1kh>87K4v+0f?dC5x_1{D(N*qcOZa|KKl}O7geF%XgjSA){U4(a-5A}y62(q1_S_65jx)98eOq}x57=i7Y*PigIWTZUi{)E=eak2As_&N@2IM%P}6@o4)L7S$2rr!zW4TGWrEIU9!TS01uJ31vqf{IUD3bh zwhJE|I+V8R`6@8 zv>YsH<6dp$nbym64A6TK4BkmxqLw?A7&2=3Hwnk8Llz;gGl|zqH@UKEgzG7))y*M_ z2eF({tU;u_yhQ^xCs{Bl>7qbMZ|>LYsd&vgj4|f8@#Lm-Ic^^tyQC4s7@D_j+)?*J@##^CC(;<4PQQOi zi&ZRsLH4_rk%6FwfMNpcrR5gh_1!7R!}zpK32&5j+@li^pi!BTvj9%6$O>Z~(M50~ zpsoxJUec}~rS9-1d%__q&Pi2HE8Y1?5eP5la_i&7a#UAhvTFbVN`-W&3oqEf6B$E| z>ymcQL6>6h9l_Kx`j>U8!wQX7?{#|btq!d+CXyzBQ2$tGIa$g0A9H};lK-X%Z93v0 zuBK$XQJV~8(oc?rp7=rnCFRJ-p>Oc-JgfOp?BOgc;BNP;Q11)_l;t`IE_OgEyM#)~ zr=Q zcU+eOuoZ$n@xggELGeb8a->HQ>}$$_(=M5{P5x{tm8S^=c!-DLj9I*jH=rc(9_*G_ z+c{&;?nArL`M5fI{_Q`4*BxfVe}a>n!LmWcE8ftIQFtoS9b<{;bzT*oIg*rDH(IUx zZ@@?~U~u|mV~s=EcY=_m`r10p^cQ66vfXBbl1dLG)ImYnKT50beJVd3d-op*_MCwy zBF&}Y-ct>Y<0st*nN)?iX!tmQK-ssY>Pw+o4mPpO@m%iiKy>vuop@4%=RYd(e<2yI zZPt2cM)GWCgSHBx2@7CQuSbiEC-tVC^{^%DBhKOmUE+U2vRrRz21*PXMHdrmM9^rZ z=8^zm52tPmKX4|>1lMwNs(!hXf3LgvH6JwQf7wwbcw_&lGuIloZ%K%w8-scRawa$% zpEb^}8~6K8otRAb_zbMm$2+U@X?CD|zs zYsAJ7@nnFB-QH4g^wOEVY`lKKr5CoPBP7&-t%j*Ccq)4ZjJ{I}G;X}wGbs|X0|dR2 z%9@i*Nl~QHPYqJ~v6Wq)!<1~XMQp5*cdxj-2Lc4sUpe?n)34H$Z(RzKL7I)peLdW+ zT#Jt1=$qGtjrUb0J;~tNy8fbIh_JU|<40l1*Rn*A?iOd33%o7+k}u>Py4WtT4)OY< zZW6>uiAFn!;C!9jtq1lyDGdt4taTaEI7;S%e6k~Li~Z~uSi(Z?u(x+?MYZn!IoVHH zRHqblR+Z)nP)oaUax4f?kf*j;Ed+>Rg_g<9ZQD=kz?rL_|ISHGIGy5^ed2d>5%j>( zxEa}(SUaEEL#lYVtOylmS$)x7nw?iY!s-&jltw|n<61ch{X*~U(s??VT_&0}oESNJS z;uVCMK88N$=jinIFZl9K+{{oVcO4gr8GeX!x$O>L{U^0A^}^b+0bTv9&>R zFz!X&=WXvN%@dg!9kU`To>0?sa(lum0MrIf&c9wW&{1JYI^4bcd17Zw4BIg-vC#!* z)e>t6hlsm}TCF&04R5VLbnNDk5h$Zi+MnLC-ffUkjRy{D)KifVXoqyeV-elibT0Fim7J+}| zCD7=ynD;)n4Z3%pz?0pzJl3iaE$n9i08isZL$U?ZF6ZdeSQdyP+k<}`FJF&gR4F;c z-f*LCy3q{2n zD0)ecHd6F_72IHxa8po>J{H)W${jx@*ZaRnp6T!yqpz~RC7owHVzZAJe~+I)7I-if zc*=+)q?UW@>jB%872Q6G8+vw2vSS4Nuh1jI`!ft&x_+o_z4zKdldANg#IbhRcMFOC zuBBWoJDl^3?qDGEEn|S-GzS>ZpzACow6qNtJiBN0#+O?>H>MUrCLINo=g-oNNZXxR zMQ85CvWxB-;OhhNV6(apaHy&~(eYWq&+w6mXZ==g>AhQ%q+}D;z(=mGiB{j!VlOvgizntNBtDN`h(;qX@~`rB(kTeAM* zuJ{*059HaD<$ulGMrs^_qIh^Q_W;IirzNd{*IwrGxJ-DCKjPU$5t(6dV2Is^_HT1iUh0 zg9fxntc@yp8V9u~!k#~bHRC`@KI$M@Io|SM*@#C~T!*bZRa6{kM)bqXH>?2?GPP>_ zXPXbIkF%Eqm7Hm%i5~HS*xMs`rl_}Xap_9l*L#fr2 z#=b%2PNMN3MD|&Rg3Wi{Tp!20m$nCfM_zT;b4om2rE&>7VweJoJh`dvzbHNPzU_bU z)=Lor(R*WzhR5OKyUL#LiKf`+<$4Ba$05~|rdzYbux4RZ5d8UtrL1kZR!58Moxuz` zVdbaQV7IoEpKcL8!VAYJ+H55!ug*+W3VIR20mfN<*Nf3~C?01qR11WvoRy26to7P& z!Q0!R&2J7`INT4W7U>SVSzK+*Ds$a(!a@UESARG;Kd@FFE29!wz0ZM&SntOc%od*B z&UhgIv})_>Q*bdT$dWVFEx2ag^!Xm_QCE^5>%K4Nh;1Jl|8hvUPD-HL`V>;2(2Jm( zx$)TGBsY=EKxlJfBNx5GP+sB#EpA~k9r<~jLaW1aEM;xnPEo>Fg*I9JxFXl}pM*2% zVjU^Zo-ETkg#Dqkc_+N)lhY6f@*zifcbdx19ICD5nZ!#fC&g zg!v|*^&=ybGK+9Od6}jKP^UT2I8MO}OQl)jY-`%gDlFVFeIGS^xPFP)xJsvcOS`tMdRf8INm~5cG3D;J%tf7Amb6YwGy8JkOL2Om zC_aM>mFjK3=3&|*&rXn8_I?sUvxVhN3D1=@Ad(G&a@tuSF%#>2fOO;g z42G_GAujikef#%Rx&eX5cpiTUE2kYYf4+g7fN1Zghu|mb{5lG}s0%8^`5{5l3ajB* zylFM3A%}^n5JLCpvC*~ohNt5$h=n(gH zufkA-*&zSW%bw_zV16Hmm)uRxJPFge2+HASXTyWLt{VJ$6>)g3LYro#;w6NRSVNF{CxC*SXV30kNMKLysS3!>Jg_@;<5R!}F$# zf0U4XS;2UPe!u$@z56E zA~n0SYO)|VD@~N&*|wo1W@CN!hd@ptM$F)huXFvOjTrdyB|#_pS?~-YMzgyWDaf>s z^BWA!qmFdyeiV!}?|^M&+AgkD-lWM`P}>;72|d+Qt^pLIH}}i$(63?jaT9hiEh?jRSIIZLe1vvGKKh9Lc~B? z)$YHTTJqUH4CwER{LO^?nRZRjP1#AEW!D4M)-Pdlh0ThvIxI9r$=RDLaf+329X)YG zQ=q%Q@+{W1CGHrQxIc8Aa!^&1zb71fmE4{f8P}oV#m{i#lXtdTf466n^%pD1*Y#3` ze?I$Eg?K&9`Z?aH6LA)yqdS6{HT!T#XN}l-d<>>d(l^857b8Q2)yR9(OEMH95sxH% zXtk$)eCuf3H^D>y0|Y@*S?p1+Xc$&sfPSh^xGZ>69UccWEkQUxP`>}xVK3(o@%0WT zfq-k5(#SAo>BH-&Z`Nen;tD3MGGVJvhWUg8Z z$e0E9wTAT*A3ak|zpO>k?Eu&G?>RIH&llyBM(6BI+(T?Ll0y>_t;?T#E!6|DMhs zJJ8aF!+N@DmFem0{aU!jeaSK4Z>h*>r6+I@xA`{Y*j182jcB%l{Gx7ke9uGvqDXf6 z+tPPWa4K}Q=hXS-_R8Sq8S$Lv23K!=n<+Y-8}Vm5pmNtw{W?IZsjB`u^`i z>g=8S*L?M>2#x0gOF zs>E{Q8p4_2VTuh8G%+aaj2WdG`mj_SDkT=kU6iw36l0CjQq8_zGrpm0fOQIw^!_`O zc6+e-42U4O>St_;9Jg4I6=e9KBELK33~RW6Hp7A~b!~ezbI69#j;(OUt*5IpVSF;E z2>Op~-;oK~O)%vs9g&9ShyBj`h3?5oUFpgU6}4}BEDQPblwH}pH{>muIN5pUG#KWn z_z*;V)B`(kdb>6dDC^Mar+Qe+w^Mgg?|;G9%wl%Sv1nwY6#KS zEzO~~@uya+t&sg{0!d6dRM)MWNW7H1DY;|rvDBA*R@{W`_iwoB$K9|e_bg*_tTX}6T9Bha^p;%o zdtlW}#j1OBHxeFxEYI=5>?cBWr1qfG%X`*#nEDDds!oNHWNPAnrmkV*)NVe=rXe+S z?eAXBK&Y+&N0ZEa_c5iT3zVm8xkHGzaY_pP*L+cYip431qX}7?^1nkI+FK)S2gQXa zrHyY-jlJ^C)0u?+6}OZId2$D6NdlXSW?nlJa-;t_E*w5?k8>ad$2Ih~K?*E1M>M?e zsbY5V!l^YcwXK7JMlzHnXOX7;^{knHDr{MAV22zK0O4OC0Vky{C1?!E7Kg0OGuMeG z748g!qk9bFM?X2TPvcUY(4NJ+22GOGw#T=)`RA*mt>`LL!e5hub@N>UmJ}Z}Yt4I_M~(Sc zVB|NA7S*DXxh;DR94-?cRGr+*JXNhL>m5m$ez|J#z!^W0r9$u@>S=#Xx1$i(=pQor z*eeZ>er$~-Yk~4%4jS=3{n9N4U+d(j{-%qLnaZz>vp=qDG8}oMNsnCLj`wJsaW6j% zy>}?rBdS=D%3Q<&7fGNRGT58L1Tj;Cr?_;*bf$jUJYnDUKv-8QSnTl5pV6M)7+Xzg z3hyKK2O=xv`V-59XAr>dR;m&{swC85!g~*8(47F=-C$ggLm+83Ws=)@y};i zxRpk%*OGGzjamZfElQ7Xr#nya>7ZQObO0^VH9I*FACy<@~PU{s`!^J95*l# z-fy-sMsI#n{g7;sFUc`0J$&I~mwCy(-0R6Rk&q6Qm>J+KT}~p&(MYOH6zz6)A;Bu1 zLX~kIot8EhFX3*w!ghwcvL5h{&)&4?(()NN3)z`|USk_eVEv;K>OcOsz06`VAYiT$uVCUYcbm>QKkkV6g zwCtBJVL?4zODE=rbr0F}&ui2r8ObP=NQ&rR3P`Uhj`i7+$nokaJ2yFh_;)}Q-iN=v zA41jlJrqY1f<37oVxpS?^kks!TcI5ZY5gxc9dx7u%u8QTykuH)c1^V^D1JyCK(Jm1 z>3)%u#t_TI@sz)0D{i$o-10+Zw?y)y7qoZ}O45fD`rHY95#kKb&eZXIL|6{^4+;4h z()O(6&k)|d*`VE$=g(vpk}O)v0en%h2v)V}!O!n^stDsOGCreNR%*R%%B>Iorduzw z23c=z12E4uR4Wr@*bGuE2l z;&%#p-_=F_C$QH^t#FQM`9+-V1vWQFL(RO?W4~{dJ#D|`{NFc*_bGUo-`^~SM_i}n zH`>q0W_+xihl`E8lCB@e%oi-wa@yEV_?3PPg{#E4#79=5C*QcG=6(?VCb736bdci4 z)bq%22Dx;hynRVi!Sp;n3u(SaAuZ{!i?+AsLFe>-m&TDMlezeNY?EGMF^8>j+6SXZ zj$Ngvd5XtQgh#hIk%Z#)K>>at@OYl25k&t{D5Wv_9b zIQ6WUO>M5!xwWYm7x_x1veIAe(*FG18(s0A13YojNiT(TNy3yndL{5mQO$b z(&NEMCn)$sn3s5(DMN}!!*_lOE`l)Dz#1>U?S?)jLyNGz`qK60Z4Do16et++--r{@ zOHS=)c$ounZFjzhqja?KX08@p zYLBsTyNp4DqgIts?WF?ThDMt37w`JaQJN5CSW{}2Q070L1n8fIzSZ;E2 zeL79GnpUTIs7nslwSQpJAYmatb_-sgE93;fzeN;+uB?Qem9Zoo;q8T9zvwds=qxyn& z9wVEWIO;_84q<8vjTh|UQVr<93wP;%0me{Hqy0^fSNS9mZUXf7um&t8eU-fE=tU<6 zq9y1gAlS!gw4V8uf zut@&_iiFouJT`;hjc?cTd7Q%{L37~a%{UC~WFts!yQ?ivXA3M6G_XkDthu{;1bmG# z9~!8oFl_F&^%Eq8u1cbMH7lCL1^2-s!2pT`8pm?&>}c0yC+1ko!HM%+p$+Uhp=Bqp zw!NYchjLT}iv$ZOlB@tW5)GAUjN_pZjS5co`BN}|%Pp(`CAYPB46*u-C|D#oK#_16 zX8zg_oo)?aeKxLiWAc1xuiQxOEO-0@s;t30NQ(iB1P>^ZJU{;zjHr{}5g?fGPFfNz z0p`TKH4H_wU-HHg1KW2~V3802Mf&t0V^F}F;WVdnKo6;p1m%+I);#E4lC%cJUq{s@ z!vhux5l|$U%_n$)#D~usf1r}AS-2)Z zdEwmSBL5j!fkX@z2^ml%Vv!DcJQFPbMsaS=F89w7*Je+h{R2Afp(!_N5v?imV3AM& zMZ(Dq%f+Upcct5c*6QaQgMenTSTAb%OS&v?xR^5Ebp{p*6;LEh^?a@f5ej(@nF$M| z@G@d1luUvF#Z0tWf2^h#cPj8+F3)YQ$98dL8kHkl4kc!>T3 zV^~fa4OW%V0Y!pE+4xaSpM55cBk)M%PV%9~%`j$xyxg36`r7I^{s6oxVE~Gx2xSKO z5w&nm$G~bJA{2Ri9HVg$y>de z^H|@WIA3Rye{|Qh%}oO7|A9rq2NVf$!LK^KIqWy2^sfGnz}NY`SaQw_W8H5O_|D3B zO?la1kq7`qQtq$kW3oIT=t2J_&AtC^%&Z!Va81@0BSxo)Tmw2B2`myJphz~j(ha{& zUt~~KTW@FIm#`v@fOF(w+5ICSD6H5-FfayuJ$!1rUN4I9_CE zpVey6;-7rDCv1RDdG6leS9mg={F4!>`!}BluO9Hp~{&qsF}~c5}tc;CMBI zMWO~2iMz#0DZ%6n*9G^Hnw;-;a_m)z(&&8rT%nQlZlxCsyjMUFph$kEJ=$FKK|57c zDI6AX-!?@9^G*m$;LBf`-E%gKK;S)>XaGer%ZP$DF*7ifr?E{JGUkq~4@e;#oWLrw zx~b!^&`>o6>)_D>ibOaOMvQKVe(-j$GYOKuIzv2~#V67rpKzubFAs0R`34q=4p1Zl z5Yw6UpARkV{=Zkv4ga~3y-A0fn^V`08y}SXalebJ zMKWy4Oj-(D=rVSn)(4yB1<8gl5M~i?8lw6Y(OHo7fbQ(QGk(7kK+C9=4)2 zosdyF6)X}vph#RQPu30~@;M^kZld~Pu_h6cU3H51?9Y$9N{Eitebr!*H~>XLD&*f{ zZ}8M76NKhy?l26g3w;uWVd+_!JSt?(p)i*Ki^K^il5?-h*nQz1w^tK6hnkr&&Ms7O zZEnWZNS4tL_=5yO@OnHhK#?fUsr!jF^EzW4@2uOb<^C=roA_wcSv!_J^ssb`&VV=D z`UOxVA4B#^uD(=aEm*2Vle?Fk@31hm&xJiAef%d`>w?FSU>!VeK#`#Kjak~z+Tg9~ z*=SVfWt0kfK8DB%mvdv|m9!9=U4hrO@&JmYzX*x)>5h@&UKB;t?5D!Fo==iq(#u!@ z2um<+GVXieeLe63iln@|VyC={>f5)NZxHa(;BJh2kL_!Nm^eneNfhvB0le8(K0uM+ z=3+yL3ircn%Kd`Pb;pPmJ>JwJh6oJL!>U=~4}>+UgBj^cbit zaY#Qgx57ormVu_?EMPY&yc{f&0H8=zV-C0l>hPFeXX#CgDLh{OOz5|yLPp_1b!!OZZU zrl-(u`nN^Xl$~ag5L8@0?~-Y;3RacG0Y&=n$$h1C6VBhDK|9A?>fgn4sjy07rOVFZ z9qI3Xkgnj3zDod#q*AUqSfeu4+pCx@m8!Nf8o+)rgvavusg)AZ-#$Pz39Kqf0*VAV zX@`>A8Hm`T_KjV8i{K@b#o(m?j*NgGd&4_t`VS{qBq>0VBn5>e9r?1+q#-vwUR!$h zm2_{wa~rzAJNdVSDmF6BDPOb-_+Y!QE}@C!9>B{2$^weyCHb54 zL_NmF>kl1d3F5QpE8UU%Y-dN3PfpDI0+a}NLjrPuA_>!%+ssCaaYk(ajMQ_*xT1`} zv~sJ!*p?3ZFT{~X=oGAjCl4r+O*Eo&{;c#wJ{wm9AL@QK8LHcVK6*CWsI=~FJu~&- zJ(m;!MS^|S1bO3R7m>SMepYOrbF{RvTi}lO!=iz~e8VAw>jtYzihv>oKk>O7F72kd zl_I)!9pE}#y}cZ0%?#Vq-o{JZyWcK=MN$G3DG=rCfXMPj)gs01GJp-eFFkX)bMuuh zPhig}kVc>*7A%r7ph%?sNrlaPkb9*rP*hAj#vL|6q$@31s2Be{Dp3m?M;QP4=V84H{+>E`qa3P$A}MR+n}E(>r*g){zk+LfEJsVD4nP62(4NxTA;P4>022g)OUczrQ|1_h%Y>|=s{8~Ss=?Tu7 z4Hg-&Na}zh2_ZNMo@)_|$s3A!ItFQog?8v^T{V8CS#t;;^1M6)?>a>TP$YE)FVv5b zn>_4AHPx~SJvCy)oOq}lD6HReiSl;o$(z8ck|v-?G=`Wnd$D9#XsVl}`<#AkNW(Kj zm7=CSC)OW>Km5rAZ(LUkP^7T1C2GvN#yvKjV-x+wOvMXl9=Y_h_F-lEA!Ow@=q0eK zqzxz%lF}!eqmX-(=4Q;Pc6d9{pu{x6X!PK=?D2%aV8WkrV3BkHMKZ&8+6GDd7po0F zmy;FaH`xlciE^J5z#XkQ-bhZB4c_RxE}%&6^AAu-?zz?|R2)aq@i>&PXf^ZQI-bZ= z-`l>*g;9amw(0?j^!<~XSQ2}_lTCXkjf*)#@`-?B;N4$bLgX2hA!icnUa$_HKA=b( zTP5m8GY_TWiptHnYmu6j@E!NW%519@HOS{h=$wmSkqiJu!mASz*ru0}3b^3Z&8Zj~ zN2GIl6=SGDCL=^VzSR1K1s3Tmph%|Waz`e@q(Tx7$_^@ARvV4mS-E>w(m(x2c6_Xw zu)s@f8Ul(W+1`gi7;eq=K>@UxnHS|@DZx3oL)IDZ86#oZ<~Isn%FGB*B;*Bt5dQR-RF|1K|JZ3ncast=b*J&OAUx0j#hhYx=9D1p9$>|3sw!0O+V(xj8aD6KgQgp#F)rqXtWicJmIRF6Fd}+ z?V6f1nSZA&*R>^r>|R)2PMZaoqx%CF)k?4rdu=U9pu6Ra!$ z0_Q*4(Ct~toaX)?-T(yHNfh|qnMsDAe|)8ubUNJBn`JH_Q1&~|Yvb@VcMFS?%1ejh z1rfl`bZsaJX8%W2pxbPf?BI;BrnYSif)f?_qU zDP>gdT#_aio_lc506hl2g`vrV)?bjtVOI8dHsTK93C1-zLJOFT!^32hhA4IiH1GZK|Huw6cj$z(sx_Ko2X0&H(F}%G@7~ zeg$%#*jO}VtX6u5GvR)kRSUe+F)(*a>dwY|m#RB=U7+|A0vt*1ewGWqsbFHB`|lWo z2E4Scf@VinI4a%&xhs?$@6JKDT$mj8{GX$>M| z(w-SC`gOEjwx*D12ta@nue9xdq^rgDk|`mriaYhZuT*ANZDFh{GGs?J9&6y5^mV&Q z4%ENgEYZZ9ecQ+2iAo8M=M17@7?VoZ8i^kfenAnu`}H|aep47~pVGO9;xMWt$A`NL z!OaRMkHMRZf1T=|V6C{Hj6%2S+NfNgLaf=wF;Z%8VIPU2w^Q8$0ly5}WvM>qh`~H& znEijG>@nXWA2Kox2cr*{isppp?NJd*=Yd)P)BD{sXJjiES zj0GG`a>aFzH1Kj-b%z*%y`_h!w3!8|1wmpfwka!&3o1Kn}&44J4JYIe=-oGm<(2U@{ zd#$9&K^zuP$7ifKn*WL-p^daI#ypDy=Zl^e)tGvRz{YNaGH|uHWE=@J;Ro z&#zY58DXrk7G$L5vKC#_J^ZZht5giudus#K_*c{O)hBg-l-HVofC$&!ihyTR0Xz9U z0c>vyi^{!|j;Y_-3o?ZM#eL;xgot=`&*Kc98S zg%|Px{IT0yHXJ>>$SCm()v1W}nh0jt?)9wLbV{pykct`=Vc0UJc7qAD#7N3>I5ZVx zq;EFr8Jtl^SELa=76^dvKTk>fuBC3SdSnzdd3BiBmQuJ+bme(guC8KofXi3OSyGT( z&o!iWz6q`IA{!IzH_NOK*1;nm%tlba?z1P&%KF`*OPyjvZCcOnnHTSK8q)u-oq!Yf zQ4}PZ;%|^WrBUL^uiZU|(|<(yM1y*miX~X1J6yriAszHp0ymjJ00*P3yhF``qNt@F z3>6*I_kSY=KGXgW0gDkH~s0Fo*}jbY`tJu=ER)BpJ#wujio*`()_ zn%;#pUwMLi%piz1CjbJNEUjp4Rm@;$#Gh9BiUzrO9W?AM8E+o;Q+F{s5!p9cQHv&z zOwHprBxr!j51w3%$9iSQ{=v|-M_B^YrVK?iiEqLQ_F5ghHJ}h+mI=$rZ(Otp9gfP> zR>^vH3X4l>QJs$RM&bTgUq~`jBk63%Y)pkVa&smp;B%3s8w0dI8)-U#WIGRo(87La z7UpPrI&hMG!EJezi>LH0WtT%HR7L|SK^P!`AiXxGa)_;}9<*LsWCk6y-_0a8Vuu(_ z9Ow0v?ciE2-vGRRIiVA{v;L2Ki8Q`uj;dyItiHf)M=vOY9zNlgMS329#Pba~U z?K>XG_v*@BZ7MZR<2=sW{Of`0lr+)P6p^GtZ!s58&$bofEkKMUCY~9J2`W9A2GM=A zOF$4TEXY$$I@4T7filcuA-~IR`d&psi@ad~4gbvg?xKijWFG|rYz7eifHB0*pO2M! za`!E{UUc*NwM5MEN{F<`ry6t%yX4T&1S})@Kn4XVns+T_IvEX65|H6q)T$V=)6QBL>b1L_W${!q?N*tL zl3fd`CK&o|6lq$WfdDkjE{oPb;Y}+WxHaYF7l?VRH8-!%rQmS3N=f5LnG5>qr!-2x zufUd&3gfv8(8&V0D5K`wTdek z%<+(u>`oqTh0;_vlw+)(3g$<ix{}Xp5t2o+)W_B8UJalMu-!MyWFbT z_@!@CRhxZ?fh1%>ZF*UfVLI}DI~wr6~Sb2@jbg)xH(Jqy&0MeMrJf7YvI z0um}o3ZCF44RR3PP_x0j_J7aXB*5=#D*m+j@z&8E0#-Lqn{Ml2diGbQdt~o(hNS|A zal%oo&Pd@kHoKv7eqM0m7Ae5bf)uxdf}QKhi|cCnco>(&Sh-veZ^oKXUJ7-QG-6j4 zCm|w-pUwKm_V5=E#)UbhmA*|G@*NQ=i-k=|@j~1N(-8;lq`%$9Y8Gl2bL;HTIEOr? zut}~Gf2p(MS*-Omu^y&AY#kXv-v_AQi~Py@Q~Me_vi?MV3nCCeYOD~{@xE1@T_SN< z8Pt%cV8=GC8S1)!wsw@So)eyoxcARcPTMIF1R9_Ci|vHSq17F+Eb2<{u3s|zPDA1v zQe=vS5f+f0dj$<;3=B9C?S3nHNdB(`l7yLBIG1f2v((`x<-EtZ;J1GBFq(TdBh&DF z?|*GL-23rX{AsT4Kmh*>2Pu@E96I)zbKAidGhXgO6aI{`WQV}@4Shr8bqSoX7i7l{ z=fR@#Mu2L!ja^}bqGV2uvc}5&vKLKA;I+C-$HUh+N4i&{SmJt>0fZT33rkvVwXCu#Fh(qHpLi2T&Ks%71W?IFi#*rlgl6y=~BkZW0<=Q z6CJX_CCiv1R)%;x7o~_^=9NA+JigNvFNdC713TD)#2-m2F9r)_U0Hbk(TK3wuyy85 z__?zfB;4X1HsPN)NI!1MwbeeY6*t^J0Y%7Xms3?w%YVrP{YL2e?zH}VU9m)CWQ-_x z%uRk}uPuH14_#g1K!Cg!ALD0Aqd+jl)*YOBW~8x-mVB*v@75g0#he0&m|m>a4r!J7 zKKBU{;x}oRilE{UfCTm(Z7aA`zdlxzd)f7cn#B@+0DJ;O+|J}9_PqGujx0ar@d%!o zq^U{CWkw4kMaVnKoHvc88o$B{8KFH%fYq(_F#rQE#APoOk_rf*HE#Hjl9=x%and0e zb?~*il|x3kc3#PNT<^7h&3o`-by8t z#h`U&bBpJ+St~*@#^iaFEx2#!LKh2{kjE>bd>vmF1N3t7#<4BB?v z81oPheRgeg2fKF#0<1tRAP59&`LsbdHi@Fz)|RCQuJ zdn_`wSQKRbBkwf;0%#CFTdbr%(D?QU%MKOEbF9*yf;{CLw`}n-n>iydj+OT&B4J?X z09JjL;HPx*P`ESV-~C1H+8424x5ZNuAAZUIU5`=pa6G{H#1TN%8qJ}$U<8LUhOfh} z&eYeVGz6xqq4k7Vq_Ms0`3friS$R-ru6S`nBy00v>X-in{3sNfrC*2DWwGA&s0WcK zT$%X$l#r)0er3woCli17RR87(3Mgb1b9HwhXz(%a)3gZPx-t{++UcmJzNt7N1pF8iaqB3O?CoZty8RuKmRU~zT+781snrr?k( zf9tXb)^GG2$M5;bhY5R$&)Zz^lZN!^q|8;$SkZ$`5CrJKpRMUq9WbZ)Ime;iM^w>U z#=iq5klZJiWjKot~5lP+cNSJ08EMiQ|#dD9!;`(MwgWX9aZX0 zw9$&NxKOEW^~W@IfB=o!tROYh^{O|MGRncPYz4v8!g^*;r(qic{GD+K>>?kOa^SsM z!hCG_3R!u7943GEWQj3IR5r7)Jh@3lsRc7Uu(1@A*GO5a>)zO`3W@KrrdP& zF7IRnI+fCiT2=Tr5FpS$l!b|enatIH&V}`KNZRu)$vqCA(r|{pRprm?g+s>Ze6gI5 zT6Zp8z&liwe5xH}5GGAIgja}B@N$GH>d%+LH9D<;Z8Qj*{r7w!$1O;FRSz|;EXAAX zLS?uc>Rf}GJKgfiq0oGVB2<5x@}g&^T{iUCJUoBre7O8;y&VXk499ub?(d5opfr1g z)6tQ7iFYNBHE1F{Y30kXd{3a%9+%%_>OpD=7_R0Warh-$f^rjMX$9WoS!E^z6Ab{@bM7NN z+(S}Ichfl|#P`W~Z?^c`J|8cH{-kX{c03w$UOY8fN z(i^*ab6Oe+lR{@O3X^}8Nxk^8Cx%J{1Kw#+Jq`W{G@qvWmnnuY9S>_mjq`msg0_tA z0!@2MO-!ZHj=}cVhtsY0Q^EA#PGx>Hl5AC&~xA@!d{=H#;Gh;a&=%CtW8ZvrwNUA}2v_|JAa5?gCe+P}ij z<`+X53m%Whj}5A@rUn1~f+>l7&6;_D*|J}qs_J1WY$BcE8E<<26Q7UW!Mkbj$F1-S zJU~Vp+Kb33L$3+c3hUBz)JeEbE@WAZ3hUjG+nV0LYCp?rVn$` z1O6)X-@6us{OK zIKn8YZ?UL;wgUe{aYRsv^8?%aVWCx|klGYiRQn288Kr{T>MQtA%fsuv*;N2-bs9Da zT66Bl0sS^Yb+ZARKGu%C48pt-g&+uv>s$!Nmc7OMxSt@5@+nk>88K)J`S8$~mb2E= zZ%eSTR1|j4D8kZzl{aCw zZ$gEC%*~th|67E^<0~rK5 zU%v1U^E8-GDd|n&BwRyviA}vgs<>v-)r;2*AM4Jg#$0Wgc_09o6BQcN^MJxDcy*g-t4p)65g65mQYg@}zB|VUWp10LN6_)92tAda+V-2JuXU29YRmHWhU?mM$v!?Ny*FKUmOmfXT_7N7Cf|?+N=ra zf69U~7W$7dGyO*&pcZm-d0jh8Vu_uH>iV!db$?S47$ZOQwUIMzD zJO)GK@*b~x;KkM1DxsL$6N+aHYD$E&CHA$eEelMYnW(YcW){f%caR^DOVx(bo3A4N zTv8gW09^JH(yKBxnPTAAuOosT9R>9*$0#&*o#Put1T83efB<5l**K)$CIHoDhF#rc ztG3A(+o1Pjco=-IQ@4XVh9Xt=ka!iNTX41pq^Is%_cb; zk736aabHsjU0TAL`&n0fBeF)T1<2Ak9=N4ibRzuYa?eP4ZOI;^9)@AMwclQ{D)n!# z9GR$ffc&1OOYaHN;lgy2Pg@NNHByJcSv8n-(Prss0-fQ^*Q$MDvJ+WEnF;6@ygrk6 zacZHz6~(NcZ}}k@ZN~r~$oku{eTfb2 z0&EreX z4$ptl@<>i$EIN0#Voiz?_Zg=Sk&G8zAH=!{*a+r!4E)P~2*x$noo%;1I63xp(Dxb` zBZ`a$nPVDpfAhclYhuZBWW62oe2ODp_@3iCTfSIk9{ICu^az^He#?}~N8IFHF$_$0 zX*~?nwSuH_#bLbu(Odv}^^>lx1pPces`{QS&_-f>Zv2SlWldPCRV-hGuH1jA47_=4 zpi|}K*Y}=lH4p&#if}SO+Xz#lIP119QkU#Hp>G611&uOERtU$k2Z)t%j!%Y!$V`Ub zi6I=4%d5Twco*>bH(7)Gma3yV7nKNhIqPBH|#%p~iCCro0 zlxYxp{C8s(B4yo>dfARmJ&Y9PcQxWCQgm3PLiia&VL$@}z~$?ZxlZHrkKo6mchznp zA#a*dvQhRZ5I+D7aRCAofLm*?9$U>LZoUoe`%B1stH8!wb%`%bfu zdRhw)%oTV6Kma(XQ%>pDWM|uL4eh$&BEIadhI{X}KqYnfA#pb%BL<0NvA3q z#H0C$_K$7TtpAjra7o@U0QT2**K!q;`j^!>Z^t8r0i@gk=#CQU|P{=Mj+00-R$R%9je=1Ev6ieUIxju%(++f69y zo?o1CDvq(gWl=&{#4C}{=%}NZ5JlAQ(;z(;h{?>zoP&XYfSSuT$%3&I2@ejp-=Jku zAxQ6){*^_;MV2M|x|oEgu=(2vXPjk=GYe+{Z5M+ICz2uwcJSD}buS?OdUpLDh;*ln z^=7{*I}$q%%JF!dG&yETw*F{AeJPH$jvcc{A#)*^H;!Fu>5Ddf-Ig2BQ8B>!rwn)D zxn2&REXm=-Is=m6q4Wn+kCA*4oDj`$410ssIUY(xpRdd6hp5MX-`OzQ-nhv1Q`>cA#tf z?|&Ak9%afK)p`o%9}8ns8dtt2WfX^YWZ?3b9EI17|^QZ*^XPpL}-v>5f&HX z7l!m?d2UX@2^>4juzzN35%5Xh_D=)& zw_XXe*Du#Qg=#MY28a#jy*Oi$J0P~WO?LCKbq<6lpggAG3Fc*`Y-3t9tell`wyG?7 zE*UYh&XN4=R2MupMh*7r|4=&(Bjx_n@f&-Q-MpgdJRAf9(7WdBl7*&j(!$p<_Hfvl z6Lz!-cNDC)@(e5?nONbOt=Zkm&JxIeWr)d3boK`Xuxnvp8$)|f z3TW&~hx|$T9bRNPLhLopPevjOFHfelDP&brD!qRE9_E(H*zao7i#9TIy)cW%(k3^L zQPh6(B=lbs9S{(j(3q3AYtYK!&)Pm%Gqj82y=IrBzFN789E4;8*+c*=KjTS39~1Fw zI@Kq8?2dvjA(;HTrtv)!|^Owm4cw%Jy5A9RCX%9Yd5Ae&tBUnc`ma};%~=L*tq+8 zZXD0Un0E5xhu1j8Zr@=t&T$XC5HYITL2gMN82Q6lEQbAfJEn6po$ZT z=B7Ygz!J_`J#snKi@-Mn%WtlE%iG1?X~m-fueSSMkLT!vm7cMBV|k^;tD!j0{P?a> zh=5czSqU8v2r$mXtGQ>~VqykfpogJ2H|Ns_>m?Rs@T#qYdzgwMEn`~u4V;}m7vCht zan--t4;@H#M}Vmm&BF~Y@G^W_u()19c3FEKmG{9cAlR-fuk^J3o#dy&F5{oi#x%?P z6$5=$K>b>~SIz?hL2qSoV!DZ|w5Rd=Lr%Cf{g#YPk>N+JD>eUhUQ!q3^WyMx@kRz4 zgUTOPUModCN>}HyG@POIK(=(hG=$Nvv{|%aKL6cD6_2M`;bAeIz^I6f?Vg^ZMqiNI zA;R*L@-^`FzEtZbJ4P#YVWW;D4P>GdYwZ++(^FddR?Z;Vit`MVPbzO8>zQe!lGPAg z3YgO%D5e!3`rt38ye^|$6t%ck&ba`{Ir&uz49b~Xb|Z;}(7Ma@Sbh46I$0*tP6 zrekXlSI1dT6#Ng#z8Z*m0#Le9_x~h8h4*u6D-N)aEzk2282iFOvrW(IucdM+mLeX7 zfi?D0r#1SiHKmZ!h03|1Y}*Wpt==yUyZ)~IP(Qcdq^9}}1W2eKGU<;|uwhgcOoy!I z^#{~Ax6IK=gCv3dhENmWS73li>71#;OMFYj`)8rcQ4A7f=ZZgj8WTN`4b7tNwFAYy zz72@P)h51FWN)z7U`Lk}@Z02qiac}P-}6}TsO6kI7t9m$7xN1Uw7s~D!oue!HWEesvoHYn2P@1vqm82(onQ_j?KEqRUuVIrvUrC5j0)+D= z#|jT|+20ql+kGX1wJXz>%1t(EKnWQvCM?xSnSwTUSl=}oH!zvC#-&6z5kng6%r9Zk zwXv40_QStu^??9$1!FvxBHSQbN|ImT!*a_)oi1klR0t@r_g}^A9St?om*ed&;xeDs z%zh)E9n9ZYXgYx~<#RSJHfF03L+m{8 zkT<9D6Nf4J(Dxn)tk4ueARhSKA@Pc7kl?)*TANBPFHiq0|xqa}-vymv=SXnUDrzTV;ZA z-%OHUG?3s~W?cQOB@GUk;@1EKB0E@tvsZQY@8*9Gw~^nf<%ga2$O(kztOyL<^( zyp`^MQJ(C!CGKW;3Tva(Sd;=_0co46d_1zV_z)0!1v;6QN1Z#DsU1nu)kv0IP-f`y zRKLLx5IC91LWq*K7dFLApFkxr-HWdv&K7sjAnGdV2`nUlfT&X;vB(>cWH=*@RKh4t z?Y@Z(8P0ggKfxwc{x_e++ph2f`*A8xRN|?xV}3g%kK!cHFcSdV8Xqex`I;s~SVMEK zJ_})}S1{G9#F|d__)p4{M>`^2hZ~v9WF828BM=RjQCI4i0)Lq~cbhS=%`(I?8Ljiw zJ$bDKkW|V+MIaztdJkV)l>9mH)MNPS%n6riFOgE!bktn3h+2RJWm8=bdpt|Y`C6x> zQ``mZ0Hv2kb&DE~nGkoS*>(dW9u`ZMW@`-$WEXIPUtr{ZClCB*)91XH@Xqf-A~yS! zf;Pds`j5wP{%LhQ2dmY!yG5zpdR1Jcjx|c|aw(gGzo`NU5TBX0WP;1H8_|++JIGv@ z{WBzT>2VY(*Bjfy&EOd_z~s)jrK0^`T9SF64_yBxu5SSi|BGTNo{xnLKCLqHh3y%s zwcwR3rHq|=Lv?=UlO^ECbzK=$#wRQx7fpsbv zr{O^7>sDdM4^ec^TV;gxR;*ab|8x^6r(|eeNaSaV+GyQ#39`h?)#-r_H!?*V-0$)2 z@zWy-Q2M8gZ0IVwX4|F|8M(Wy@GwRW5|N!hN)V{-8&Q6m$XK7bJX^#Mjyw(1HI?|6 zcKS7jNlF^!Y>yUSVj%Q^GZ9bsl;P-&_Cg0yO)no$pV+0-VI4p9ooxIPxkplhB+d?{hc zxvFojIw$!2`e|jwyCOP@eUz)4WWuC*{Lw2|*Y&{6%M!G4x%QpjbK5mdIQ5rvTujUZH4jK9pbHqX6w z8JiQ5^rIR(|NTJ*bMNfPgK5&b_N?_8M6lXVQF@2gsu-6^kLMfcxB)uh{stPiF-ETT z9bZ=wn?ob2om*2|rHIt=D~^Bj^r%=A-W@Az^GZIqITLioFmly?g9c~L^gU_>ZMVQbGMEHi zwkaQ8(h*b3D)^v2F6JJUEPEBWD#7~OEO!6h%U7`j9Y+rc@K}o1@%{_TScWi@L zMD3jfp0R+()6Q_?+?3AmuxAsKR_3NF6ya~vN9b+xDBcHxWJ#yn5x+b6;u5D9p@v?(YMkDlOm%P-lG5i!SIGo_Hb9&{5&7opKZ9u4Nf$he&18 z*`V-lC79_3^T`}TNY}D-GX)tYeoEk(8KgF`qeidDLmxl@pDm!4h6LQ;h)^r7OG*3M z-pb#joRVE@RP}72e*Np5!GqZN(JZl-z0LUZ8vK@~$cVd|_r_ZTq(a&Crs&H;PAw-W zNSu|S3{MPxT}5a`<7!Rmgcu*ocdb85^Px4^$kZ+Qpb1l;9}h0P zl}oEDkU8o{BRRHH??9b1u}iJ-@j@_oHeZ|b*w<)_C!UZ*L(&?9030l!vPeCSGm*k7 zzWh#RYydpk1sqjCD}7Kq z5V-t{K*|rX>g=lTjNFiCaX<1adgXQNO^IedQ1O#!PMP!{jR&sUyy=cxkPV10)wN@`IaGE_s$4AprNB zW7wxaP4}mjMHY?<_nIa1xU84ys3@}%pxK>J2H)J)4PD}9!g4#y5`(HrA9^s_oM61N zBEPsX88@;7gt)=`%|?y9gO5;&@fUy4O$U9Q-iVt)~A=n z4#MXjp|J5}$5*=MwaU5e+1WM8))7U`fE0T&ngiA z?_+;6AZdhTm#g?s_i`U4#Vp@zH=1e7a-$(fUS71e7#1i#|t0u>8Pm5|^h zXQz%GyACAh8!oaBC>m)y7=#p}s$1a%Sz-z^Ea~{1Fa#^4>Tb-r(d0)a{$(|;!m=ce zvQJP7uawK5?7OFJ^I+r)WO0VJMeA94IN|+4NMof_U$pieT*0<(+7N!%@>gSn4w6w> z#2y4Ud6Ha_KLQSFv(xvx$B;&+9VIK+qxoXz=!_KvxdOdb8pK5QK3+zahd1qUdW=tE zvn#3dt057m7Voy4zR1mMj*8^Y--7{xW53+p{HF)yf}a_Xbr=`Cu<%;dMb&My?A=@y zRtKaj^^Ln!OK}%yFqN}YMx#xyn2)zCGU9gSMxf{;uM-QT9myC|DYYSL!D{h0{@DZM z`1|eYyh%!Gojq;1Ig?s4(HBvtFE)>~%{E0Fb)tZJaSBBXYG`Uq3^sM7wg30+d`_73 z)uOo5T=KJ(!$Ec(7FNoKv(aRFPG|UZ@WCqjyTqFAvcB>CyCei!tq&R*X?bh^oBl4E zp?NwjD0K|^$v7WZklxh+gFWK)%o$1DcFr6|xEa`p!NeAQ+2MXAQg}F*IsEMzqP&GZ zD5+=(l}}HvU2Z!HsMXAU{HLDf+cjy{D-j ziA5TEgwxdE?kU!=)1W{}wZeV}X(S)(#osCeE@R5smp%FL$lkoF5F#&P3Ee6qEgTvrfow-yobO zeeGXw8a@%{8s=m>*i=Q{uTzyPqt_SR+IO0#9G7wT?!yvpjkd&8X}(@18bl7!>|O^Rm`R0!%z@>cVDDZ70But)_ z5`krp`r&!ltj;P-v9Lw5Nf1;F`c{$4RIcvlX}XEZ&&)eAUhcPWypG=oJFay`XPk-Y z($$ir!upzMmHUHQ8Zc8E&6k#r3pEmfOAwAEgCz2vcw?i-(l*b-c4a5E36pn=Am4<+ zTR3k8=zl{W$(3V|GJx2;kJ^;MZ%8rsv;?_0vkH$uK+y7ipXF&loo|bNpu)JVqIPbg zW2(L?H`#@xjAx8ks7V4{U`rYGX=eySBz~00JMbR z)zfcX#qZ*t|3d}G&JGV6Fu#u5eTcuSf~W8Up55$R*W!|5x+D_2(h zs({}80pF%iFLjjR=x|8{ftz~9-yrz(AN9K>#(C*K$uphaXOZCIWItLP#cv{14}5iQ zPmiGGOL<>kj~C+7ybxcLXy18^YB43>zwEaMOdc;m2Up=LnuY+3vrtfXDR!GzCSR4_H_5hd$La4t^@~0CKv{cBE01XSI=ofN+=?Cw_TR zj#=EW;`%o>838V(7u-fBsw0-M8sW$kjUBjt;KtYEaWyq6sgAN^Z(Du`TGg zm~xNLaSC@Wb*X9#pv){(Fjz3H+ie`x0rynESn2XK{$ui39frC>Tjl6qeZrLlQzbqi z%me!657#9&#EwIb{uki-Xad@`Y=tA1k$sBuhVW;iSqu72r({Av*|pVV${|bHn01K$&`*7lH_aR}debq_Z5fbF{AU6i<>$?5mzz`gscdJB zMm(<%o}j7QGAdNnsEwk86`obJBBF3 z|FDf12mnOFs$MPWQuI=~+0}tWc*QnW*A!IC;O7e+xk4z&`Wn9KE6o^@YcJ4NdnkO! z{g!oa(g6qM3c;paA`J#2l#+n{9Xs1&dNWs9%EgA2wnYhNh;_RjS`HdnT{Y>JoFz0t z+h(>AnR~N}awuZ@ZERSEF8FsnP?QAPi{~+u=?@U#4t)lir8ZA(XX0uSl#q}&CbfZ+ zg5tKe!=+7e(ylB^jP*y^P#=#gK5}hqGX+z$9NmpOQC)qOM(l=|^m^Bd!Ct_`a^G7z zcSu^}OR6v1sy=?c0$ z6`b&fLWR~^s&+DX&bB!;z3JXHhZ$XOuYZy~qvOZ54YL&G{)xCJ!r4#f6_`=reOty0 zq7f{&{QDr(3N!K)K~b~jd)@Hs`c~yL*DVk2Cb2X;PX^OA89iMXSTGV{8TFqV|7(YN z8e5|j`4~&G;^i-i@av83M61XY7f6PG%00p#sj>Pef#ukFXo|vp%3)ybIiofvyQQNH ziqX|S6h}>%wpu#bi7rR8Z0|p*N|R&PvwJlwBx;k99tgELbt?4G0FztdC|vp(-8s1V z+w(24kv?NFZDkvjoPdKMJyMvmirAjEv1d3v;sgPm=YOqT8>PK5_%!`wH>O_%ZHaIy z4c!&W<7i%XQ>_6{b9sT-iQ#T^W5y1)O{)F1IX}s>52fEVa#$-HL+pJ@l_BST-l>Qj z&FsgX?^YS^=|o(%hwJTuBLSn<>NCGJ4l&m=gwJRUReODVaju@-*1UW~iDIQQf^#s^`gy_sw-!ePjI9%hHh&(QXHZF5SJY2#A$4Mbtf zx}Y%7R@vE~Ze5gP8i1)s1w?OMernW%L5UI<83F+aw$|T)N_DiSuO}AqZ6L|Au!?xy1EWa-l8KEg;wkj9HY!P`-IHpS2{t+TYHW*fLPxbC>XW3$%1X}aZK zl(=b4tTo(PE}_XqJuj0tP6|Gm#|sk^Gz&7>g z^}==KaVu9Y;xH;kr=v-v+YLLP_1VspskOetnm_3i{0m{_byBPj> z2Q;tSs5xOII;3FP0m#r%Tmidi(efT-JKsh!jexYzv*i*IB+1 zv8Yp-$$W95Hp$ECJZq3Seg10JC-xwNBnKeV;8Og12AnrURqrESg*gs+_}u;{Xk4VC zroL83R3?0To8O{h{L`nWY>}+|r5h8{UYK$ZKkYpD(*KkM+SSg&M`>V$1;(XKSq;Wk z+{i}m+|&mrvOA)sX0WZPtO_TEaG?r(D#;`4tyfXrjk`}aZC?dk^Xxki67f*;b>CwQv8tjM}HP#6NJj_d085O6@tfs0cJ`hX$O> zvEGxGzsDWJYZ&j~2@dT91YGft-Cfq3FjnE7<^usH<$VyZgtc+Gm^{&cu5l2~2J)H;x^tNyW)6lT~Jh)q++qhGlrgT27H;lVGCLRv??JybHj1}zNtcq2y~hLGQt@EMQ69?(u;N~H%#g<^KsRzmqH8lYn0)$ z=GN10m6H6?9aN11L@phHS^V9qoSNH3iXMp)I}5FLf(1dwW8dlts)=ST+!`3vQHxfK zug3$GFYcS5QRDeVc=}V^i580_nSy;ym+C@#5FkMIMVI9>*d-+D;Evlx@d93Zf7;4i zli%>JZ`$mns%(J(zE2N5v3xo2wx7tj(~u2ff_vULebX>-x^93Iq=pp`tJzi>d9%XH z+m_-8n_S*GxFI^ixhVB12ytr;4nyY8shd!P+NqRuPE03h^iAbk-!<2fFE0((rbvoL-`lHfLC>W>`XVO0*OnhNag_Kh^I&(bh zPFy*tM#=Mq?g#=sK$V0gB?eINcNfLIJ_g;)dz+gZ)Kz!)5cEG6A~+Q!XaCpCT{cA7 zbO8XSS-L@L=@J&{kZz<)TDrSIy1QGtyHi?Px&@Suhb}=HefbCPe!TY|Tzh8bo;hdE zEQu-e9xAbsl0|zbMNBNGpE=12m3Xu*kdFv3~!h3gu-efAKT=wt$ zy>WM*z*49crIs~|0%L|%CEdf~=_hiOR;lPdMUC&`};xFR14d3 z@0(EN?k|ORy>LU`f{Twi388^m!+pgKg9+{p zZox38MP24t;p>{HyQvUauS!!+cwisH@(j;FZ9I7iMP#m6V$scF1T90RHBt?1$b zV=?uY2{S*tD3QXJtMuXW8a3(Pox`4%pg7_KqowWqpqLZn1wyW32R^~dw+?p=+2Id) zn0<2dh}&Huk|0@Cyd%4<0X?sbbJ^>5!2mJ>)_ZAqrnHjO*SvB7g}!tacDAqpX$ezX zqWcBrg#%2LUR@$Ddd8*G23#Z;(SH1}Eq>DT^kTMthHeN)pPt!=!xt^L)CLL`STek^ z8zOePsG((P)JJ1Gi3jjIm4pB5L0=jizI1KJ@`-X_jh~Mu!E3FQ=bN zOvvb?2=2;a^d2*FQl`ld$_qy8SeSl%Xosa0JtqvwxM#c*w_mxu) zqUC(XpJ(%|WEtYVb+UeWI=mO`KewViQe$rll`EcZ1ZK|uooUXPSLqCp@>wbW4oz2& zr3#hr9gnKa1!d&;FTMzYSU(3>?)14v%E2+&84EFr2=~ zWzlPLtr@^s;TL!HT*^2T^l8h6Z-qMiLij~hrLcudeDBH{|LMlQH#+Xeds1PLk^T-9 z*|@60Ww>zU7G$IP*B^R%`2dP{)2#B%!`Lk73*IUa#Ql9Ah6)#vLTu8Dh{m|8wNj{8*Mq?24^KEv` zuDD^w(wyABjnpM*;qSI0|A7U$76RWHj*ojF{=4>rb+-7LQKQgHcS2PVCH&sJF?!k; z`8188Q55giQ>MQ*`L&OPdhR;M>NB^wo#P1`N<_jG`a+~RHKb=nXSV1qtUjpT+{3qe zvB+jtfW=+LVElZDvkJ=Ako7&`p*s^5UO1qXkW>-AJ6CwU7e=ER;WlNzvu+M^ zw<-+d5EM4H{nB`=sXwi8WN-x-niRKcf%% zDPeb0$$x`&r(bX(ek=@@KJKZW_l;?yz9M{Q`y-TrnO>t3p@(h1mXLe5$+!LAn>W45 zKW6hV1q-S#OO6O9M4kg-VjOsEV{f?7`&^1h#iN}Clgaf{pA#(dg2uaObT1dMBhiQA zWSOwga|@k9*BVdo>L3XQ-ORlA)^6P(BZ?;Ks2G+>2iDw+yFddtQ_R3D_jk({r&~gn z+W(X;vM2nRNqbOE>aesJDflzH!SF{uqh>!$UBP#Fx5NL!C9-+4Ue60;3}egntp4XtmQf?&NDR}(su-HVvx(eMB@md<*$*8I|T~=9jnmprI$nc9ojK&iAjIVA%2V{ z=9s5S*$hfA|E47=5Xw~F$^!pJnzEdQ>184$K&|N3R}ZW;o6uzo38 zY?iq|SVzW)=-lVR6iMUlI#fR^<}9nXP=PZ*nsd~5?Qr&&B1{iXbx(1O)v61c7^vE1 z;<=MY6yOV)CS*}P^h6(%W!vLQK<;Ej;WU_!%+P4eoTNEc|HMN$&Y~xj5h>EzTm|(obtS zxW65XMuA==UlV^ZXB$4V|GHO};yJX&vjAE2HuBU7ids{q?urzT-fABMRvB!sDY?qoMh8rH1KX#}{{*}*5 zaQb`eY(%j5@D{l_g=C*Pr9^VjqgOi8Ehf3zEd%ya&4q~SJVRWr+(1d^-Rqs{#igEF z2^HR_i)Z^n6kARuT^?K_?VtUBf)7VK>pzzgqifYXnT&XuC0R`_+P4mLA1g8M6bW4+ zndXj^V}B?|jHo$a#cD}VJri#+gppL~G`t*bP6m-AmDZY+G-bEja zVJFj%#~-vK!cwm0>5`*;Ey?e3_C_ih#YE2#jqOE~JC1hG;e!)Qe&-xA(g=P#R*cn> zA95VZ=Op5l6vzCVV$Lol17meM27ko2CuV$FCm!<{#n?1_s(pZ_NxP23B&>=ks~kW- zcyH__3NJ#_7P~Ir&dnEY(|;`Ux+caIKy=AN5wqg|UOoDMpFw!IjIxosM9nrVp5EeD zS@D55|PE4W8|<9A`{28zrXIH%W15(?3rr}a;IOYLhD-Xz(&7_(xq zmG~L>-BRudL^j#7t`#Hk&z4%936g7RCDq_5T4sJUA&62)JG#n7gh>*W>B?P7sIl9fBFleO)C*Ncn~9Ul z;R+OnC-nVPE-V=M&Ti*k2+jVNMeg&91uH2hqG5~DdTBWrw;AplMh=s))@bpa2D_bd z&J5@Rr~FgZNs4~b)Yi%)?H`ONvDa}Rw(l1hN5nHf1Y2LwK;pdxe=Unth{|EhslTdP z9BGEr7Wv0M!u(Fs%Kdkn&=s|r(^EWY**R0DXxv?=hdeIFIN@Bo2^)(1SzA8pBV3PB z#fSfx>>Ni9XqP`3lQR;lTH`PczVV8ZYY3a;tD`>rswa;JCp%fgLlNFiOjj5Jt)e-ZIP;2Y>tfXOe^%NzGJnG1VTwQ(f> zm4}e{Pf2VJ|&nJon7O5-k%P1aIki|l@IkJP%#ILs$vnh1yP1yaF-6Dq0D zi?7h$&>crbi@?na|5lcpi>Szr8vXTc9#}=Q*mHdvb8lToYgBfllAlA5{X4vGQkYZN z_UtAZ5ezf3J-4ZK4RF1_nu%rVIPr~;Zz#aU4r&Bt(CChqID*axW}56s$=jps3u zQ)C6i=#QKjF}Spv`f{WB5VWambx;5=7pVvLEi>}NX-=Tdy6Pw z29jXPTtIk($<}Bwc-VHD(jPxHe|$Z)#mN|0vsbsE2auR!n?35VA4=d&PWJbVxP_MAM>ik?hv|W@wEpL?>P`>;hJlv;?e_T?>sySJkq*h#C?Fjo-X-z|siOA1aUEe>va?53w)p9o4+QAR(9WwaXveaf=rxDO1ua<%=C27 zy4G;4l%vRhi>5@6nAY5_^OnQsI!2Q?=q_8z_L^^Jt#_a4$(~?hPET1bY|K-h1gbv31hD>j)}q(+KP{wDNw90=y^;9lt5qet`jY2SJcn!SlVNMz*GA0=~- z$wt#CpEOw+Krwi?Pm+mV!gt!P^EfG9Mygb%t2vV&uoKT_*yZ8nZTxKsph@BDc@S4rz z4ykML$^w0;zs>aiz9Gt%mLh|(J(hajflx8}Nsom99rctoChzxj_aEcKY2ptvtP#6Q zxK*5e*q1@GspQlkBvI>i+297@qJ>>J6Af?jEL8IB*K!$o>UZ&l{Uv>BUJ5?#uR|6; zE6Qw{l1MZP@@X5nn3TrLY}iiWNk|A^yM05*FyyRN=Iga$ngZH;Wc^yD>g|q7RLZgq zIW;qFikz_vOr&^=*c^5W8*%2;SWpL%Mgq#=CNQV=I&#hjvm*2c>rb6%yvD7v?JP5V z^eA-4@&1%PJK+V5ayk3*g-~EDQI_IRPp^g@Na5N?|p!Fsl!8c%kYq>09mtPH-zQj6A|R?nHW z-mv)ir5r^6O_w`07K<>ZCmbf*LFYBpEs%$_Il@HJh?DPpKq6wDvp&j z>qAK;;jXL9h4g!;d`tCJZ%pidibSoyc722JY3_b1Y)Wgv1UQEn{388bF_3^H+>*w| zJB9gV(=#iCE4J-hu^D?JyB;}a=4Itf8?pDhL@HG=7_Y&3qXf(3qR~30dK$w-TjN43 zJWAhc)mAWC`U&4GsFf^G@AVNQfq*;jPP(+em2HC0{B7vV4k4B*`)GT@jrRD%%~XD` zy|x4A%c*B4OBYF~l|fw=`N*1%eqBmVQTQsGtBuTVbs)|sIFLc`5+1D^7#@#z;^+t(DgurCzX>K`Zo| z|Jj{=F*Pi@cUx`2SW>_~tjt>ZJA6)Sx&FFWj)U0AAdsnPbCV16?zOb*&in@5Z6o~a za{O5jcb@iJ$*{l4aVS{KiPTUN*|DX9dX%1yPT_ukrie;x?+t|B3-&kc!eKM)I)_kq zj`#XQj++SXv`sL!Oq*#gi0t7DBpwH* z_qFr=HOJOH5a#Zx=`y9vfXL%a%UG+w3K41-+#!|}qCmsp6{kq_A~KwBF(Ag2pABA@B9$ucyQD^8`q6)~*9Q`Lg&XOivRTg;1%dxv4LkSYz3J5x z_3_fOIW@-4g)jcdkn!(2Ti~b1KncP*oAquCC5%*R6jdX-XZ+^@7F0%d63&r#@6gIH z2vHAT`i?5NKOoG&{NLwUVFB_yE6nSU5+<%S0e?~ZdS}dLR+y>wA%WhjE;MHeIV$18 zg(l&kBLNB!9H2mKR34Iv+4CB3-DR*u!{4SKMpO&GcPm)7uJcWQnke>#0t62zkcuz2 zF*q3ZHg|A`&LDPEB=j?rIWK7Sr&*va1smNA4-_B-K!M`NZ6{Gb=xAIOfv6mScEilMYToJ6qU=+M#3r~% zwTLDRxxL}<$;n>@nnvjWC_w0d0`dGfvAPMWN)C|wWYB?Mw8>Re=)0VDTj)`5V&%3= z;sFH+15h9&=k1B$VIJM%$!QCo9H*9W@@cluME57~r^xjL zvg%Q8c!r)5YamF%ryZ!vew!1#aQi2U1O*5SP#|mJB9NOthoN@Sv!V0q3eva7C?eJ3 z2`S5*_@TK)PCY0<*nk2>IAM8^Lew|Ty>hPCNC@O4Mqqf3=Co!4#aMkH${-ESi z)>H?~+(V3-0Rd_58XX4j4+>C#@BjtkZ<>Zc2rHVB@3uAF!QRR|TWuu|yM@skEzaGa zX=|RK0O11)OM}z0M8{)RAKpKX=}1RDO8chKU>)vZSy_L`ynVm zZvX{~@h5d9(Z*<_hp`?h+0QYo9DiB2y$YjP)xPaoXZYVC7P*l%6NbRL=GrWjCg3(^Mbdxfv1Z=g3(p{ zLhM9?jbOew(O`Nx`)&m^a|jG55KVX=z3&28=tpJsV8{zvL=j^7qKSK!kf!5k3jt0U z7Zeeq02C-Zgs~`AVL2O#gd#%5USE_c@ z9QEN;^%(0x(cBCQ5H+Ac_#PWNx}SZpM7ZXJif7Ez!(GPWF+bdBj5Z;NO^v14K>?xx z6bRhDR<8BXyo5%UFoBK8nHaLVXR}F9B5trz*qS=FD-8vR7EmCChQ5jXxC8HB@P6xW zMdLfPVz0TyzJi7(hlF9?tLZpE0ipvG2w9VAM8dlOWW=LBDOb~bLdnXHGB!wIGXP1& z`AOy=3xBHH2XtbLA*D2vT)&@5zG4Am-Y&VE62z+(gy2wOeatPpeDEEytevQdG%f}5lx znoGlyvMa{>Bp&`f4T=ac0SaWYa2x;fpYdaY`-EBI;6-K_{zZ_31O`ZR+#(fZl<4m-?sT>Lr3!p$`1Mc}9oo@KFqXL@Qr%8L8OF#Mwk-Hf8 zo21RuVKn%m06_o+f;YC->9@@03`=bx6~p~N_5#ivDU6L+$;tim{cD=-Cn!LyfC35H z`YM(=r1|YfD9)yciDh&me_08-rc24m5tUVuM<|5?^bSxUIN0T9-%2`Lm8bUu0upOi z-mdgX&mZOIes7H-%FJ%wK>=a|6i5qGc&2@W$iT(d<3N45A3d!t=Zo8E=wW(ja9-z( z#WNHjc0hqZgUX)~eS45^t4td9B4gEclFCp|>8DWRr@F{@WeuPy8gl>&q;nY* zPD!uDlfKtq!u3RxD*ZlDs{yu^9bsB-3yKJF0t$qg4>H?fVEj`i zl|f(_UV4D_-9-xu5Er08%FP_{4~@JCV~p#lOQF7JvwmcCB#dRE`#ePyNeyq6pa5|L z3S{!R6`p+9)MbQ1Up9zcOSODV2UmYS5pFYGZh zb>98BvS&b5CgWNf^?*3i7?VO%H0A{q2-6??P&|pCqk()5Oxr0l(QbaNLm1o6xvY>t z)A^ndnye2Wpg<8BLXIIGWtQC@mPV8kRc5zT(z9g>N;o70#EaAiROe94A$~xC=SDUnDV|>=)s$fP{E2f8U;>wf>-Anb(opsw_63x(b1V z0wf41kahA@%Uh}sT7f+SxFdwvbX;g<(WsYGY7mQYJo~SBCQyKc00k08`JOjk_k8Ra zAbJ|)=5{lEa9V-s!zRdds8eY4-%klBK*E3mA%TO7wkUMTx%f#r9_qGlx2YQA?Fkbn zZ6QlK;N3w3C_o~B0;#v3e<#JVmfPjC3A3=CK zeEYl|MRDkn*Ue7Kv-h-E)P1h+14D(*!l3|30Se?!NMSi0qv56--}p^2GVl{SIf0c6 zO>~*xugp}e+?5q5K+=E$v5pISGa&w#pudCSQE+JSfWaeowz^jq(6WA<4u4s-00l?} zP#`8Q)N7}Q)-C($uN&b{HB9TFcH{ClVk&lW-EceAxtdUbWB~<&s{%787_8Gf>!bUO z<5sg!+5B3Z)PsF4T(jmmCOI*J0`vh;Aoa@2w_JzY!jC2<oWQNm*LP=FKw1+rMp>L&PdK#mt2rDPRnKXO@_ z{QX-}&R4s1P4k}(rWsIx6afWt=N#%i_Gm=CqT3s4NiTFGV-M3TKZ|%EM%M@#iE91= z1xN`{Aj$U)Rq#lVNF3~UYA8eUe~Odvvgb@PvHq3o$)J8kgyw3DGN3>xo3;~f-l}iT zJnhHqEE(o87C7Q#b4G=#Vc{}gu*RvOh>!}PKyT@3ehUrx!YpYTVM`Eb%FwWG7!?b| zllUxXe^nG>uY>}m3Mi1smpE&XfY!8N8g3=#TOxNvWt-3U6v-CQfFs=aAsJD(f(06s_&aV@O-u%sF*{6^((ofK{9CQIl8=m$J^lz-d9(IB0^e#0->4E*s^n5tes)}AYkJj5vZBs zbS0Gz>tT4~X)9S02TiMhHlRS<;yc}jz1ppC2A;t+bBSp|sx5?aH5VL&=wIv6wR^as zh>#ATK*T}OBT?S^gDhiwQ#nt2O8bgWZjixTY_*n*Zb{~e-%x;b0R=)4cyBf5)nG7* zX0qZQ_rS?*uSqP2$nvYJC2hJ1ygmg5NDojT?tmg~`l8GS5ex0B*@S0ttp}7Kb)pdX zt@!NtAC&s4P=NFS1;YA949_YbYPqhSMJ%;~qclkH2K4h$go$InMv5{>37Q(~CqRKn ze@iD=tm5|7cs@$BSFn||<;;wkwCtMn2HC{EvM9ee*K*#cqLND6cVL2(7%krV1@}KK C*0m@A diff --git a/packages/testutil/testpeers/testkeys_pregenerated-22-15.bin b/packages/testutil/testpeers/testkeys_pregenerated-22-15.bin index 6ae447247ac9f2394c2ea2fcd545438defc6c115..2e4e32abc7327826cf4457c9309c48ba44605f48 100644 GIT binary patch literal 9454 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 literal 9410 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 diff --git a/packages/testutil/testpeers/testkeys_pregenerated-31-21.bin b/packages/testutil/testpeers/testkeys_pregenerated-31-21.bin index 7103af67a660d53654dd443a023ad64642dd0d9c..c33b30cb5d3d924665b1976bbfd29cda9f0e8051 100644 GIT binary patch literal 13303 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 literal 13241 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 literal 1748 zcmZQ!h-G06xMX;y_9n-fNR8rV$+gW(pNK88IhIy;W$*DA6Zy!*LRy zO$|-&)Mt-pSbm#SxlrrJzBjV!rVd(J%T~_*p#AWX<>Asp z3zu25he)}6tCxSGE?lC1Ys#O8A%*wnxL8k-Uvg>g4A(^~%}#HFYY@A5p8h(eT8-qUYL`KMSe^<*N6w*_|-IBbeOuA!e=e`K3BP8SbwT+{^Q*vySWO z-mV!(pE~mwF34CJ*1%wDbJk?TvKyOb{o$+@I;Zxj$-_@@rs&+W9C=m?rXDH%?3BX3 zJ5_7-S-%Ts0$=3^U+qY~|MFw{@0+#kckDD78F)5 zcjLRee8gxaZ;X{H2Ue)jGcU{&N?x2$l7H+F5t$+P7l|eEu!oJjZ!g zk-{mx{h#aP61=n?zUlRLkj^iY(tUC#az)+-&k2byukSh-{eYjl)BD!V%b(vCnCP@C z~&BVa9TlKsTJ10DS*1!4p^)Ko9z$OFA6 zoy^Kk=j9i1G%(mtxqQs6KT{_pS+;#||4WX2KO7YPXfd%{hkdnC7I=}YIMeP0Q{;ii zNwPl;re$y^%?e5CRQ1tycbomu?S8=1#jz6uu1tA&ZQ*YTHxK1W71jaTM{moTJIwkO ze*5v_W11hEcW2l<3t2g3{lw6f^QYfA<$W;S*@^K(fkky+Eq89r$p(hr$_<}-RA0&) zeg3`pa5j(pZi7yqZ@Mv8J{$L|hwE%Gx1Dln&yJn8kEi-gYe_HtCcDIEkEUHr@00gU z_g*hu_WxWQzfM5p?9(?K_a7?c|L}$Xwc(DVZ&;3QO_hAI)~)c!mG)zW+F7Djryn7sd>dxeuQ_TuQ1g1S}ITx{lgkd4lsUOXbWF`EVZx9e_pPFZ zRBE}*HL7VESr=BwB$pFW*4eVKI)B0GNALR&yr1WJpAQ5WgMbhr6lQgm;19DjOmXok zqxkihnfy1f5TT@?G;_xpcn$zSev}R*P6oii6~r!^E^VxFy_EWiAJT)|2VF*Ga2b&J zpc0H24TK@donGAVWp?81k$mOst19?Ti_pgi*U_jnU6fw3)lGxUk(2kZ&T7IuVzOIRMPt+!>AC%IND%Fl-@#AkI%35^>j&HM+0gxAH<~!4KE-`( zQg}LUo9p4H#)R$qnqd?XYXC+E2#DJuot!NlZN7V+eXWzBM0xwf(&gbI)+4!P@tU!e z=&YkTh4B#YhzH0(F{Y4RRVlFy=K$-j>U$2llgsM%Y4wFN=8ilf+xVJxghgGEber(` zB|f(C6j4l=Me5ZUrro`i@bdlb3@`?ziVtc_BV zALOh>tIjHEO`vn77X-399S?gm4mO!mHGF){GzO9noqib$cLgnMt!R0dI25_A;-Hi4 zl)@lGp!_o1kQ!^Lo~gV1-xEmZpBT>=p~n3V{WpB+hP%>VolHr`)ZPc1oWC#nIp%o}uV73{CvbG{j7hfJ7eTljk~362lnq66m|`}1&txUXpEIgJOc zCG*iQ<|Rfx4lugOOD|7I;v>h+i7N)X0vQNd(8o;?@1oQCDw3sJrLXGi6ra8z$(yqJ z24sbtY4JSHgCo=Kyt~Z9_Tf#mT1%^B8{Qk9!0yKLGuWWTh$a0X(nENS(iNqunt^}dSDoNVgf^S)aYQwt%&&n6D|%{ Oc5jwkUg`e`0N(+(|9ad2 literal 1878 zcmZQ!xX;3Pwn5eQ_zp$kq$?M<%wcq^D{kMsrf-AFBekx4Ipc=NM+^)sK-j>*o%|&> z;N8+NvCvEX=9SX>Wdi(i{|9foZ^mXO>i;$Ky`fWMANMnHOa7(PQ`aW(8u`nATF7y~ zYfVRrdqGf(^U-Ej;jAB?tD}|PO9(mE&i^n|O7+&-x&@CU*H+!+a}Z$ARCl@<9qPX{ zHTvG-*HM=7&fj?)cl;CLe5`Ug!8AcMU@*9$fq_{-w0rARY5}r9jMNgOiw7JrBi#`7oZmkcEU`eT7z#eB@wO!U+hO z+g>Cxw9U?p^SUBZZn1DzOVWP#FPCcPv>VCyVa*OrMe% zI6r;y+}YyxAMeJ@NoTPszjn(-HfQZ^Z=St#P4BQ7E;;@9_+ER7$uk#3J{xtYVMnO+`5+B9*N~5`jye|?CG&p0 zd!dwn`6J^k=S!wdSd=#VW8LJJS0CR3rG&h6d4t^>e)RVAZFWg9Oyy<$AG=%g*9+;NTeYcKP1=d4&J za=}MSSUmZ6Xv=%i1)@%;udppC@rabvkE;-l`Ls%AS{L7(60?@6DsCGExj4-D7z+IO zuAjYb+Q8s7=eGL2-jkIFEQ(pSy$#7^vc36xNrs8CQB(BIcVF}t$olQdSZ5;FUg*Bx zti=0-(yB`)>q?gIoc{M@VtnIEfpe-yZuMtm?~*&*FFM2CO5>ohtK_rnwA>Sy*8RP; z!jS#2`8t`S7uG4937u%0_Tu!y&R4%|Cq7_hJQu*Yj`#2O$7u}=T`wM+w{M%ZQsqLY z-GyVemlL<==9ynf&2Or`em%vt(WTHKj(g+L)>r8)+vQelD*3UkKzwr1Zi|ym=Hcfp zEjCScxw-iHwusnvg}$HZt97^g?c9~i{n?@_UE=oD|KELz(?vU3C+%XGc+XY9CvBn zjhMNuHQ|EuB?~64=b;DpCZ>I2+@=yd)hRu)uVZCapvKls9p7%rB{jd|TFHB-g+)Mt zq1}wnOU)uiOC#ln?upOW{akG{m-mR@eHeGZ@;J{Y%gyH*2o`XRgQS3C00IRD`?Jw6 zw%=OOZvFG1&jKzHL&rZeKWzwH*10fiqv*TiJ_ba1h-pxHsO7ez<6J$Z)*lo3m(Fwf z_HE&OS*2N5cE_n~d(KuSK3S9q4>1oa4~e{z-^f#V_(03Fr?U>*{Wftp_fFI4m`C38 LUZ1Qr;niON>Hkxq diff --git a/packages/testutil/testpeers/testkeys_pregenerated-40-27.bin b/packages/testutil/testpeers/testkeys_pregenerated-40-27.bin index e19e3a142d8833267d3058a695a0c995ceb4d614..2c686fc0ce1292e8eb8ac669f1e7c165f25acb95 100644 GIT binary patch literal 17152 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 literal 17072 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 diff --git a/packages/testutil/testpeers/testkeys_pregenerated-70-47.bin b/packages/testutil/testpeers/testkeys_pregenerated-70-47.bin index dc6f5d90fb66f4425aa90b25ea44add6aa192d65..9b3ebaa7ab9000fa41ab1b6a4a20f240e877f0e0 100644 GIT binary patch literal 29982 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 literal 29842 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 Date: Wed, 8 Dec 2021 16:36:46 +0200 Subject: [PATCH 005/120] Take Chain Nodes from the DKShare. --- packages/chain/chain.go | 2 -- packages/chain/chainimpl/chainimpl.go | 13 ++++++++++--- packages/chain/chainimpl/eventproc.go | 2 +- packages/chain/committee/committee.go | 9 --------- packages/peering/domain/domain.go | 19 +++++++++++++------ packages/peering/lpp/lppNetImpl.go | 6 ++++-- 6 files changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/chain/chain.go b/packages/chain/chain.go index f3be14593d..133d5f9460 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -8,7 +8,6 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" - "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/chain/messages" @@ -84,7 +83,6 @@ type Committee interface { RunACSConsensus(value []byte, sessionID uint64, stateIndex uint32, callback func(sessionID uint64, acs [][]byte)) GetOtherValidatorsPeerIDs() []string GetRandomValidators(upToN int) []string - MemberPubKeys() []*ed25519.PublicKey } type NodeConnection interface { diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index fed4f37aa4..d4e809fc18 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -150,7 +150,7 @@ func NewChain( ret.eventChainTransition.Attach(events.NewClosure(ret.processChainTransition)) var err error - ret.chainPeers, err = netProvider.PeerDomain(chainID.Array(), peerNetConfig.Neighbors()) + ret.chainPeers, err = netProvider.PeerDomain(chainID.Array(), []string{netProvider.Self().NetID()}) // TODO: PubKey. if err != nil { log.Errorf("NewChain: %v", err) return nil @@ -268,8 +268,12 @@ func (c *chainObj) processChainTransition(msg *chain.ChainTransitionEventData) { } func (c *chainObj) updateChainNodes() { + c.log.Infof("XXX: updateChainNodes...") + defer c.log.Infof("XXX: updateChainNodes... Done") stateIndex := c.consensus.GetStatusSnapshot().StateIndex + c.log.Infof("XXX: updateChainNodes... 1") govAccessNodes := make([]ed25519.PublicKey, 0) + c.log.Infof("XXX: updateChainNodes... 2") if stateIndex > 0 { res, err := viewcontext.NewFromChain(c).CallView( governance.Contract.Hname(), @@ -281,6 +285,7 @@ func (c *chainObj) updateChainNodes() { } govAccessNodes = governance.NewGetChainNodesResponseFromDict(res).AccessNodes } + c.log.Infof("XXX: updateChainNodes... 3") // // Collect the new set of access nodes in the communication domain. @@ -289,13 +294,14 @@ func (c *chainObj) updateChainNodes() { newMembers[*c.netProvider.Self().PubKey()] = true cmt := c.getCommittee() if cmt != nil { - for _, cm := range cmt.MemberPubKeys() { + for _, cm := range cmt.DKShare().NodePubKeys { newMembers[*cm] = true } } for _, newAccessNode := range govAccessNodes { newMembers[newAccessNode] = true } + c.log.Infof("XXX: updateChainNodes... 4") // // Pass it to the underlying domain to make a graceful update. @@ -304,6 +310,7 @@ func (c *chainObj) updateChainNodes() { pubKeyCopy := pubKey newMemberList = append(newMemberList, &pubKeyCopy) } + c.log.Infof("XXX: updateChainNodes... 5") c.chainPeers.UpdatePeers(newMemberList) } @@ -384,7 +391,7 @@ func (c *chainObj) getOwnCommitteeRecord(addr ledgerstate.Address) (*registry.Co } // just in case check if I am among committee nodes // should not happen - if !util.StringInList(c.peerNetworkConfig.OwnNetID(), rec.Nodes) { + if !util.StringInList(c.peerNetworkConfig.OwnNetID(), rec.Nodes) { // TODO: KP: XXX: ... return nil, xerrors.Errorf("createCommitteeIfNeeded: I am not among nodes of the committee record. Inconsistency") } return rec, nil diff --git a/packages/chain/chainimpl/eventproc.go b/packages/chain/chainimpl/eventproc.go index 972459742d..5e848b356b 100644 --- a/packages/chain/chainimpl/eventproc.go +++ b/packages/chain/chainimpl/eventproc.go @@ -101,7 +101,7 @@ func (c *chainObj) EnqueueLedgerState(chainOutput *ledgerstate.AliasOutput, time // handleLedgerState processes the only chain output which exists on the chain's address // If necessary, it creates/changes/rotates committee object func (c *chainObj) handleLedgerState(msg *messages.StateMsg) { - c.log.Debugf("handleLedgerState message received, stateIndex: %d, stateAddr: %s, state transition: %v, timestamp: ", + c.log.Debugf("handleLedgerState message received, stateIndex: %d, stateAddr: %s, state transition: %v, timestamp: %v", msg.ChainOutput.GetStateIndex(), msg.ChainOutput.GetStateAddress().Base58(), !msg.ChainOutput.GetIsGovernanceUpdated(), msg.Timestamp) sh, err := hashing.HashValueFromBytes(msg.ChainOutput.GetStateData()) if err != nil { diff --git a/packages/chain/committee/committee.go b/packages/chain/committee/committee.go index 801cd59625..027422dbda 100644 --- a/packages/chain/committee/committee.go +++ b/packages/chain/committee/committee.go @@ -8,7 +8,6 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/ledgerstate" - "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/wasp/packages/chain" "github.com/iotaledger/wasp/packages/chain/consensus/commonsubset" @@ -176,14 +175,6 @@ func (c *committee) PeerStatus() []*chain.PeerStatus { return ret } -func (c *committee) MemberPubKeys() []*ed25519.PublicKey { - ret := make([]*ed25519.PublicKey, 0) - for _, peer := range c.validatorNodes.AllNodes() { - ret = append(ret, peer.PubKey()) - } - return ret -} - func (c *committee) Close() { c.acsRunner.Close() c.isReady.Store(false) diff --git a/packages/peering/domain/domain.go b/packages/peering/domain/domain.go index bad2defb75..6850891ff1 100644 --- a/packages/peering/domain/domain.go +++ b/packages/peering/domain/domain.go @@ -98,13 +98,17 @@ func (d *DomainImpl) GetRandomPeers(upToNumPeers int) []string { func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { d.mutex.RLock() - defer d.mutex.RUnlock() + nodes := make(map[string]peering.PeerSender) + for k, v := range d.nodes { + nodes[k] = v + } + d.mutex.RUnlock() changed := false // // Add new peers. for _, newPeerPubKey := range newPeerPubKeys { found := false - for _, existingPeer := range d.nodes { + for _, existingPeer := range nodes { if *existingPeer.PubKey() == *newPeerPubKey { found = true break @@ -117,14 +121,14 @@ func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { d.log.Warnf("Peer with pubKey=%v not found, will be ignored for now, reason: %v", newPeerPubKey.String(), err) } else { changed = true - d.nodes[newPeerSender.NetID()] = newPeerSender + nodes[newPeerSender.NetID()] = newPeerSender d.log.Infof("Domain peer added, pubKey=%v, netID=%v", newPeerSender.PubKey().String(), newPeerSender.NetID()) } } } // // Remove peers that are not needed anymore. - for _, oldPeer := range d.nodes { + for _, oldPeer := range nodes { found := false for _, newPeerPubKey := range newPeerPubKeys { if *oldPeer.PubKey() == *newPeerPubKey { @@ -134,12 +138,15 @@ func (d *DomainImpl) UpdatePeers(newPeerPubKeys []*ed25519.PublicKey) { } if !found && (*oldPeer.PubKey() != *d.netProvider.Self().PubKey()) { changed = true - delete(d.nodes, oldPeer.NetID()) + delete(nodes, oldPeer.NetID()) d.log.Infof("Domain peer removed, pubKey=%v, netID=%v", oldPeer.PubKey().String(), oldPeer.NetID()) } } if changed { - d.reshufflePeers() + d.mutex.Lock() + d.nodes = nodes + d.permutation = util.NewPermutation16(uint16(len(nodes)), nil) + d.mutex.Unlock() } } diff --git a/packages/peering/lpp/lppNetImpl.go b/packages/peering/lpp/lppNetImpl.go index 6cd4faf086..0eb6bf15d4 100644 --- a/packages/peering/lpp/lppNetImpl.go +++ b/packages/peering/lpp/lppNetImpl.go @@ -381,13 +381,15 @@ func (n *netImpl) PeerByNetID(peerNetID string) (peering.PeerSender, error) { // NOTE: For now, only known nodes can be looked up by PubKey. func (n *netImpl) PeerByPubKey(peerPub *ed25519.PublicKey) (peering.PeerSender, error) { n.peersLock.RLock() - defer n.peersLock.RUnlock() for i := range n.peers { pk := n.peers[i].PubKey() if pk != nil && *pk == *peerPub { // Compared as binaries. - return n.PeerByNetID(n.peers[i].NetID()) + peerPubKey := n.peers[i].NetID() + n.peersLock.RUnlock() + return n.PeerByNetID(peerPubKey) } } + n.peersLock.RUnlock() return nil, errors.New("known peer not found by pubKey") } From f1e6685a2f2068fbd3c0883ce602e6d3ca9b19dd Mon Sep 17 00:00:00 2001 From: Karolis Petrauskas Date: Wed, 8 Dec 2021 18:53:13 +0200 Subject: [PATCH 006/120] NetConfig removed, peers removed from the ChainRecord. --- packages/apilib/deploychain.go | 5 +- packages/chain/chain.go | 1 - packages/chain/chainimpl/chainimpl.go | 52 ++++++++++++--------- packages/chain/committee/committee.go | 42 ++--------------- packages/chain/committee/committee_test.go | 34 +------------- packages/chain/consensus/setup_test.go | 29 ------------ packages/chains/chains.go | 14 ++---- packages/chains/chains_test.go | 3 ++ packages/chains/dispatch.go | 3 ++ packages/dashboard/chainlist.go | 30 ++++++++---- packages/dashboard/mock_test.go | 2 - packages/dashboard/templates/chainlist.tmpl | 6 +-- packages/peering/config.go | 47 ------------------- packages/peering/config_test.go | 29 ------------ packages/registry/chainrecord.go | 29 ++---------- packages/registry/chainrecord_test.go | 5 +- packages/registry/providers.go | 8 ---- packages/webapi/model/chain_record.go | 10 ++-- 18 files changed, 81 insertions(+), 268 deletions(-) delete mode 100644 packages/peering/config_test.go diff --git a/packages/apilib/deploychain.go b/packages/apilib/deploychain.go index 4f71778df4..2776956f6f 100644 --- a/packages/apilib/deploychain.go +++ b/packages/apilib/deploychain.go @@ -83,7 +83,7 @@ func DeployChain(par CreateChainParams, stateControllerAddr ledgerstate.Address) fmt.Fprintf(textout, "creating chain origin and init transaction %s.. OK\n", initRequestTx.ID().Base58()) fmt.Fprint(textout, "sending committee record to nodes.. OK\n") - err = ActivateChainOnAccessNodes(par.AllAPIHosts, par.AllPeeringHosts, chainID) + err = ActivateChainOnAccessNodes(par.AllAPIHosts, chainID) fmt.Fprint(textout, par.Prefix) if err != nil { fmt.Fprintf(textout, "activating chain %s.. FAILED: %v\n", chainID.Base58(), err) @@ -162,12 +162,11 @@ func CreateChainOrigin(node *goshimmer.Client, originator *ed25519.KeyPair, stat // ActivateChainOnAccessNodes puts chain records into nodes and activates its // TODO needs refactoring and optimization -func ActivateChainOnAccessNodes(apiHosts, peers []string, chainID *iscp.ChainID) error { +func ActivateChainOnAccessNodes(apiHosts []string, chainID *iscp.ChainID) error { nodes := multiclient.New(apiHosts) // ------------ put chain records to hosts err := nodes.PutChainRecord(®istry.ChainRecord{ ChainID: chainID, - Peers: peers, }) if err != nil { return xerrors.Errorf("ActivateChainOnAccessNodes: %w", err) diff --git a/packages/chain/chain.go b/packages/chain/chain.go index 133d5f9460..db8fa5101f 100644 --- a/packages/chain/chain.go +++ b/packages/chain/chain.go @@ -181,7 +181,6 @@ type CommitteeInfo struct { type PeerStatus struct { Index int PeeringID string - IsSelf bool Connected bool } diff --git a/packages/chain/chainimpl/chainimpl.go b/packages/chain/chainimpl/chainimpl.go index d4e809fc18..296984008c 100644 --- a/packages/chain/chainimpl/chainimpl.go +++ b/packages/chain/chainimpl/chainimpl.go @@ -29,7 +29,6 @@ import ( "github.com/iotaledger/wasp/packages/publisher" "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/state" - "github.com/iotaledger/wasp/packages/util" "github.com/iotaledger/wasp/packages/util/pipe" "github.com/iotaledger/wasp/packages/vm/core/blocklog" "github.com/iotaledger/wasp/packages/vm/core/governance" @@ -65,7 +64,6 @@ type chainObj struct { log *logger.Logger nodeConn chain.NodeConnection db kvstore.KVStore - peerNetworkConfig registry.PeerNetworkConfigProvider netProvider peering.NetworkProvider dksProvider registry.DKShareRegistryProvider committeeRegistry registry.CommitteeRegistryProvider @@ -97,7 +95,6 @@ func NewChain( chainID *iscp.ChainID, log *logger.Logger, txstreamClient *txstream.Client, - peerNetConfig registry.PeerNetworkConfigProvider, // TODO: Remove it. db kvstore.KVStore, netProvider peering.NetworkProvider, dksProvider registry.DKShareRegistryProvider, @@ -122,7 +119,6 @@ func NewChain( db: db, chainStateSync: chainStateSync, stateReader: state.NewOptimisticStateReader(db, chainStateSync), - peerNetworkConfig: peerNetConfig, netProvider: netProvider, dksProvider: dksProvider, committeeRegistry: committeeRegistry, @@ -268,24 +264,24 @@ func (c *chainObj) processChainTransition(msg *chain.ChainTransitionEventData) { } func (c *chainObj) updateChainNodes() { - c.log.Infof("XXX: updateChainNodes...") - defer c.log.Infof("XXX: updateChainNodes... Done") - stateIndex := c.consensus.GetStatusSnapshot().StateIndex - c.log.Infof("XXX: updateChainNodes... 1") govAccessNodes := make([]ed25519.PublicKey, 0) - c.log.Infof("XXX: updateChainNodes... 2") - if stateIndex > 0 { - res, err := viewcontext.NewFromChain(c).CallView( - governance.Contract.Hname(), - governance.FuncGetChainNodes.Hname(), - governance.GetChainNodesRequest{}.AsDict(), - ) - if err != nil { - c.log.Panicf("unable to read the governance contract state: %v", err) + if c.consensus != nil { + statusSnapshot := c.consensus.GetStatusSnapshot() + if statusSnapshot != nil { + stateIndex := c.consensus.GetStatusSnapshot().StateIndex + if stateIndex > 0 { + res, err := viewcontext.NewFromChain(c).CallView( + governance.Contract.Hname(), + governance.FuncGetChainNodes.Hname(), + governance.GetChainNodesRequest{}.AsDict(), + ) + if err != nil { + c.log.Panicf("unable to read the governance contract state: %v", err) + } + govAccessNodes = governance.NewGetChainNodesResponseFromDict(res).AccessNodes + } } - govAccessNodes = governance.NewGetChainNodesResponseFromDict(res).AccessNodes } - c.log.Infof("XXX: updateChainNodes... 3") // // Collect the new set of access nodes in the communication domain. @@ -301,7 +297,6 @@ func (c *chainObj) updateChainNodes() { for _, newAccessNode := range govAccessNodes { newMembers[newAccessNode] = true } - c.log.Infof("XXX: updateChainNodes... 4") // // Pass it to the underlying domain to make a graceful update. @@ -310,7 +305,6 @@ func (c *chainObj) updateChainNodes() { pubKeyCopy := pubKey newMemberList = append(newMemberList, &pubKeyCopy) } - c.log.Infof("XXX: updateChainNodes... 5") c.chainPeers.UpdatePeers(newMemberList) } @@ -389,9 +383,22 @@ func (c *chainObj) getOwnCommitteeRecord(addr ledgerstate.Address) (*registry.Co // committee record wasn't found in th registry, I am not the part of the committee return nil, nil } + // // just in case check if I am among committee nodes // should not happen - if !util.StringInList(c.peerNetworkConfig.OwnNetID(), rec.Nodes) { // TODO: KP: XXX: ... + selfPubKey := c.netProvider.Self().PubKey() + cmtDKShare, err := c.dksProvider.LoadDKShare(addr) + if err != nil { + return nil, err + } + found := false + for i := range cmtDKShare.NodePubKeys { + if *cmtDKShare.NodePubKeys[i] == *selfPubKey { + found = true + break + } + } + if !found { return nil, xerrors.Errorf("createCommitteeIfNeeded: I am not among nodes of the committee record. Inconsistency") } return rec, nil @@ -403,7 +410,6 @@ func (c *chainObj) createNewCommitteeAndConsensus(cmtRec *registry.CommitteeReco cmtRec, c.chainID, c.netProvider, - c.peerNetworkConfig, c.dksProvider, c.log, ) diff --git a/packages/chain/committee/committee.go b/packages/chain/committee/committee.go index 027422dbda..aed36fef8d 100644 --- a/packages/chain/committee/committee.go +++ b/packages/chain/committee/committee.go @@ -23,7 +23,6 @@ import ( type committee struct { isReady *atomic.Bool address ledgerstate.Address - peerConfig registry.PeerNetworkConfigProvider validatorNodes peering.GroupProvider acsRunner chain.AsynchronousCommonSubsetRunner size uint16 @@ -41,7 +40,6 @@ func New( cmtRec *registry.CommitteeRecord, chainID *iscp.ChainID, netProvider peering.NetworkProvider, - peerConfig registry.PeerNetworkConfigProvider, dksProvider registry.DKShareRegistryProvider, log *logger.Logger, acsRunner ...chain.AsynchronousCommonSubsetRunner, // Only for mocking. @@ -54,9 +52,6 @@ func New( if dkshare.Index == nil { return nil, nil, xerrors.Errorf("NewCommittee: wrong DKShare record for address %s: %w", cmtRec.Address.Base58(), err) } - if err := checkValidatorNodeIDs(peerConfig, dkshare.N, *dkshare.Index, cmtRec.Nodes); err != nil { - return nil, nil, xerrors.Errorf("NewCommittee: %w", err) - } // peerGroupID is calculated by XORing chainID and stateAddr. // It allows to use same statAddr for different chains peerGroupID := cmtRec.Address.Array() @@ -76,7 +71,6 @@ func New( isReady: atomic.NewBool(false), address: cmtRec.Address, validatorNodes: peers, - peerConfig: peerConfig, size: dkshare.N, quorum: dkshare.T, ownIndex: *dkshare.Index, @@ -158,17 +152,10 @@ func (c *committee) QuorumIsAlive(quorum ...uint16) bool { func (c *committee) PeerStatus() []*chain.PeerStatus { ret := make([]*chain.PeerStatus, 0) for i, peer := range c.validatorNodes.AllNodes() { - isSelf := peer == nil || peer.NetID() == c.peerConfig.OwnNetID() status := &chain.PeerStatus{ - Index: int(i), - IsSelf: isSelf, - } - if isSelf { - status.PeeringID = c.peerConfig.OwnNetID() - status.Connected = true - } else { - status.PeeringID = peer.NetID() - status.Connected = peer.IsAlive() + Index: int(i), + PeeringID: peer.NetID(), + Connected: peer.IsAlive(), } ret = append(ret, status) } @@ -197,29 +184,6 @@ func (c *committee) waitReady(waitReady bool) { c.isReady.Store(true) } -func checkValidatorNodeIDs(cfg registry.PeerNetworkConfigProvider, n, ownIndex uint16, validatorNetIDs []string) error { - if !util.AllDifferentStrings(validatorNetIDs) { - return xerrors.Errorf("checkValidatorNodeIDs: list of validators nodes contains duplicates: %+v", validatorNetIDs) - } - if len(validatorNetIDs) != int(n) { - return xerrors.Errorf("checkValidatorNodeIDs: number of validator nodes must be equal to the N parameter of the committee") - } - if ownIndex >= n { - return xerrors.New("checkValidatorNodeIDs: wrong own validator index") - } - if validatorNetIDs[ownIndex] != cfg.OwnNetID() { - return xerrors.New("checkValidatorNodeIDs: own netID is expected at own validator index") - } - // check if all validator node IDs are among known validatorNodes - allPeers := []string{cfg.OwnNetID()} - allPeers = append(allPeers, cfg.Neighbors()...) - if !util.IsSubset(validatorNetIDs, allPeers) { - return xerrors.Errorf("not all validator nodes are among known neighbors: all peers: %+v, committee: %+v", - allPeers, validatorNetIDs) - } - return nil -} - func (c *committee) GetOtherValidatorsPeerIDs() []string { nodes := c.validatorNodes.OtherNodes() ret := make([]string, len(nodes)) diff --git a/packages/chain/committee/committee_test.go b/packages/chain/committee/committee_test.go index 400e5c4302..10f4b280e0 100644 --- a/packages/chain/committee/committee_test.go +++ b/packages/chain/committee/committee_test.go @@ -4,7 +4,6 @@ package committee import ( - "fmt" "testing" "time" @@ -26,16 +25,11 @@ func TestCommitteeBasic(t *testing.T) { nodes, netCloser := testpeers.SetupNet(netIDs, identities, testutil.NewPeeringNetReliable(log), log) net0 := nodes[0] - cfg0 := &committeeimplTestConfigProvider{ - ownNetID: netIDs[0], - neighbors: netIDs, - } - cmtRec := ®istry.CommitteeRecord{ Address: stateAddr, Nodes: netIDs, } - c, _, err := New(cmtRec, nil, net0, cfg0, dksRegistries[0], log) + c, _, err := New(cmtRec, nil, net0, dksRegistries[0], log) require.NoError(t, err) require.True(t, c.Address().Equals(stateAddr)) require.EqualValues(t, 4, c.Size()) @@ -47,29 +41,3 @@ func TestCommitteeBasic(t *testing.T) { require.False(t, c.IsReady()) require.NoError(t, netCloser.Close()) } - -var _ registry.PeerNetworkConfigProvider = &committeeimplTestConfigProvider{} - -// TODO: should this object be obtained from peering.NetworkProvider? -// Or should registry.PeerNetworkConfigProvider methods methods be part of -// peering.NetworkProvider interface -type committeeimplTestConfigProvider struct { - ownNetID string - neighbors []string -} - -func (p *committeeimplTestConfigProvider) OwnNetID() string { - return p.ownNetID -} - -func (p *committeeimplTestConfigProvider) PeeringPort() int { - return 0 // Anything -} - -func (p *committeeimplTestConfigProvider) Neighbors() []string { - return p.neighbors -} - -func (p *committeeimplTestConfigProvider) String() string { - return fmt.Sprintf("committeeimplPeerConfig( ownNetID: %s, neighbors: %+v )", p.OwnNetID(), p.Neighbors()) -} diff --git a/packages/chain/consensus/setup_test.go b/packages/chain/consensus/setup_test.go index a68de5c336..2a3a6450aa 100644 --- a/packages/chain/consensus/setup_test.go +++ b/packages/chain/consensus/setup_test.go @@ -203,10 +203,6 @@ func (env *MockedEnv) NewNode(nodeIndex uint16, timers ConsensusTimers) *mockedN mempoolMetrics := metrics.DefaultChainMetrics() ret.Mempool = mempool.New(ret.ChainCore.GetStateReader(), iscp.NewInMemoryBlobCache(), log, mempoolMetrics) - cfg := &consensusTestConfigProvider{ - ownNetID: nodeID, - neighbors: env.NodeIDs, - } // // Pass the ACS mock, if it was set in env.MockedACS. acs := make([]chain.AsynchronousCommonSubsetRunner, 0, 1) @@ -221,7 +217,6 @@ func (env *MockedEnv) NewNode(nodeIndex uint16, timers ConsensusTimers) *mockedN cmtRec, env.ChainID, env.NetworkProviders[nodeIndex], - cfg, env.DKSRegistries[nodeIndex], log, acs..., @@ -444,27 +439,3 @@ func (env *MockedEnv) PostDummyRequests(n int, randomize ...bool) { } } } - -// TODO: should this object be obtained from peering.NetworkProvider? -// Or should registry.PeerNetworkConfigProvider methods methods be part of -// peering.NetworkProvider interface -type consensusTestConfigProvider struct { - ownNetID string - neighbors []string -} - -func (p *consensusTestConfigProvider) OwnNetID() string { - return p.ownNetID -} - -func (p *consensusTestConfigProvider) PeeringPort() int { - return 0 // Anything -} - -func (p *consensusTestConfigProvider) Neighbors() []string { - return p.neighbors -} - -func (p *consensusTestConfigProvider) String() string { - return fmt.Sprintf("consensusTestConfigProvider( ownNetID: %s, neighbors: %+v )", p.OwnNetID(), p.Neighbors()) -} diff --git a/packages/chains/chains.go b/packages/chains/chains.go index c6dc0958bf..453f1c0f1d 100644 --- a/packages/chains/chains.go +++ b/packages/chains/chains.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package chains import ( @@ -13,7 +16,6 @@ import ( "github.com/iotaledger/wasp/packages/database/dbmanager" "github.com/iotaledger/wasp/packages/iscp" "github.com/iotaledger/wasp/packages/metrics" - "github.com/iotaledger/wasp/packages/parameters" "github.com/iotaledger/wasp/packages/peering" "github.com/iotaledger/wasp/packages/registry" "github.com/iotaledger/wasp/packages/vm/processors" @@ -126,15 +128,6 @@ func (c *Chains) Activate(chr *registry.ChainRecord, registryProvider registry.P return nil } // create new chain object - peerNetworkConfig, err := peering.NewStaticPeerNetworkConfigProvider( - parameters.GetString(parameters.PeeringMyNetID), - parameters.GetInt(parameters.PeeringPort), - chr.Peers..., - ) - if err != nil { - return xerrors.Errorf("cannot create peer network config provider") - } - defaultRegistry := registryProvider() chainKVStore := c.getOrCreateKVStore(chr.ChainID) chainMetrics := allMetrics.NewChainMetrics(chr.ChainID) @@ -142,7 +135,6 @@ func (c *Chains) Activate(chr *registry.ChainRecord, registryProvider registry.P chr.ChainID, c.log, c.nodeConn, - peerNetworkConfig, chainKVStore, c.networkProvider, defaultRegistry, diff --git a/packages/chains/chains_test.go b/packages/chains/chains_test.go index afc22a205e..72aa0f8c50 100644 --- a/packages/chains/chains_test.go +++ b/packages/chains/chains_test.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package chains import ( diff --git a/packages/chains/dispatch.go b/packages/chains/dispatch.go index 3688bd5a5a..d9e64ed87f 100644 --- a/packages/chains/dispatch.go +++ b/packages/chains/dispatch.go @@ -1,3 +1,6 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package chains import ( diff --git a/packages/dashboard/chainlist.go b/packages/dashboard/chainlist.go index 468c935a90..75ba816a10 100644 --- a/packages/dashboard/chainlist.go +++ b/packages/dashboard/chainlist.go @@ -1,10 +1,13 @@ +// Copyright 2020 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + package dashboard import ( _ "embed" "net/http" - "github.com/iotaledger/wasp/packages/registry" + "github.com/iotaledger/wasp/packages/iscp" "github.com/labstack/echo/v4" ) @@ -42,11 +45,20 @@ func (d *Dashboard) fetchChains() ([]*ChainOverview, error) { } r := make([]*ChainOverview, len(crs)) for i, cr := range crs { - info, err := d.fetchRootInfo(cr.ChainID) + rootInfo, err := d.fetchRootInfo(cr.ChainID) + if err != nil { + return nil, err + } + cmtInfo, err := d.wasp.GetChainCommitteeInfo(cr.ChainID) + if err != nil { + return nil, err + } r[i] = &ChainOverview{ - ChainRecord: cr, - RootInfo: info, - Error: err, + ChainID: cr.ChainID, + Active: cr.Active, + RootInfo: rootInfo, + CommitteeSize: len(cmtInfo.PeerStatus), + Error: err, } } return r, nil @@ -58,7 +70,9 @@ type ChainListTemplateParams struct { } type ChainOverview struct { - ChainRecord *registry.ChainRecord - RootInfo RootInfo - Error error + ChainID *iscp.ChainID + Active bool + RootInfo RootInfo + CommitteeSize int + Error error } diff --git a/packages/dashboard/mock_test.go b/packages/dashboard/mock_test.go index 52b674ac64..f0dcf893f6 100644 --- a/packages/dashboard/mock_test.go +++ b/packages/dashboard/mock_test.go @@ -112,13 +112,11 @@ func (w *waspServicesMock) GetChainCommitteeInfo(chainID *iscp.ChainID) (*chain. { Index: 0, PeeringID: "0", - IsSelf: true, Connected: true, }, { Index: 1, PeeringID: "1", - IsSelf: false, Connected: true, }, }, diff --git a/packages/dashboard/templates/chainlist.tmpl b/packages/dashboard/templates/chainlist.tmpl index ca835a5ffb..df232753b4 100644 --- a/packages/dashboard/templates/chainlist.tmpl +++ b/packages/dashboard/templates/chainlist.tmpl @@ -15,14 +15,14 @@ {{range $_, $c := .Chains}} - {{ $id := $c.ChainRecord.ChainID.Base58 }} + {{ $id := $c.ChainID.Base58 }} {{ if not $c.Error }}{{ $id }}{{ else }}{{ $id }}{{ end }} {{ trim 50 $c.RootInfo.Description }} {{- if $c.Error }}