From 8e2fa92236486f3f47d95e9385c596630845c4f0 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 10 Sep 2024 16:30:47 -0400 Subject: [PATCH 01/26] chore: Remove paladin-testinfra_besu_data volume on cleanup Signed-off-by: dwertent --- testinfra/build.gradle | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/testinfra/build.gradle b/testinfra/build.gradle index b5e3d13bd..2a015f531 100644 --- a/testinfra/build.gradle +++ b/testinfra/build.gradle @@ -40,7 +40,14 @@ task removeBesuBootstrapTool(type: Exec, dependsOn: stopTestInfra) { ignoreExitValue true } -task clean(type: Delete, dependsOn: [tasks.removeBesuBootstrapTool, tasks.stopTestInfra]) { - +task removeBesuBootstrapVolume(type: Exec, dependsOn: stopTestInfra) { + executable "docker" + args "volume", "rm", "-f", "paladin-testinfra_besu_data" + errorOutput OutputStream.nullOutputStream() + ignoreExitValue true +} + +task clean(type: Delete, dependsOn: [tasks.removeBesuBootstrapVolume, tasks.removeBesuBootstrapTool, tasks.stopTestInfra]) { + } From c0629b793d2200b5ff8b8d2a8cc9afbbe77cc9e6 Mon Sep 17 00:00:00 2001 From: dwertent Date: Wed, 11 Sep 2024 15:01:26 -0400 Subject: [PATCH 02/26] using -v flag Signed-off-by: dwertent --- testinfra/build.gradle | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/testinfra/build.gradle b/testinfra/build.gradle index 2a015f531..ebf611395 100644 --- a/testinfra/build.gradle +++ b/testinfra/build.gradle @@ -30,7 +30,7 @@ task startTestInfra(type: DockerCompose, dependsOn: tasks.besuBootstrapTool) { task stopTestInfra(type: DockerCompose) { composeFile 'docker-compose-test.yml' projectName 'paladin-testinfra' - args 'down' + args 'down', '-v' } task removeBesuBootstrapTool(type: Exec, dependsOn: stopTestInfra) { @@ -40,14 +40,7 @@ task removeBesuBootstrapTool(type: Exec, dependsOn: stopTestInfra) { ignoreExitValue true } -task removeBesuBootstrapVolume(type: Exec, dependsOn: stopTestInfra) { - executable "docker" - args "volume", "rm", "-f", "paladin-testinfra_besu_data" - errorOutput OutputStream.nullOutputStream() - ignoreExitValue true -} - -task clean(type: Delete, dependsOn: [tasks.removeBesuBootstrapVolume, tasks.removeBesuBootstrapTool, tasks.stopTestInfra]) { +task clean(type: Delete, dependsOn: [tasks.removeBesuBootstrapTool, tasks.stopTestInfra]) { } From c27f269d579f070ebd08b0002eae7a53932bb83f Mon Sep 17 00:00:00 2001 From: dwertent Date: Wed, 11 Sep 2024 15:02:52 -0400 Subject: [PATCH 03/26] rm line Signed-off-by: dwertent --- testinfra/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/testinfra/build.gradle b/testinfra/build.gradle index ebf611395..2d7105146 100644 --- a/testinfra/build.gradle +++ b/testinfra/build.gradle @@ -41,6 +41,5 @@ task removeBesuBootstrapTool(type: Exec, dependsOn: stopTestInfra) { } task clean(type: Delete, dependsOn: [tasks.removeBesuBootstrapTool, tasks.stopTestInfra]) { - } From f9f1d0269920bdd42f9fd628403d0d699d79c25e Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 16 Sep 2024 19:28:48 -0400 Subject: [PATCH 04/26] noto: add additional asserts in E2E test Signed-off-by: Andrew Richardson --- domains/noto/internal/noto/e2e_noto_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/domains/noto/internal/noto/e2e_noto_test.go b/domains/noto/internal/noto/e2e_noto_test.go index 8e2424d87..e09cf97c0 100644 --- a/domains/noto/internal/noto/e2e_noto_test.go +++ b/domains/noto/internal/noto/e2e_noto_test.go @@ -190,6 +190,10 @@ func TestNoto(t *testing.T) { assert.ErrorContains(t, rpcerr.Error(), "PD200009") assert.True(t, boolResult) + coins, err = noto.FindCoins(ctx, "{}") + require.NoError(t, err) + require.Len(t, coins, 1) + log.L(ctx).Infof("Transfer 150 from notary (should fail)") rpcerr = rpc.CallRPC(ctx, &boolResult, "testbed_invoke", &tktypes.PrivateContractInvoke{ From: notaryName, @@ -203,6 +207,10 @@ func TestNoto(t *testing.T) { require.NotNil(t, rpcerr) assert.ErrorContains(t, rpcerr.Error(), "PD200005") + coins, err = noto.FindCoins(ctx, "{}") + require.NoError(t, err) + require.Len(t, coins, 1) + log.L(ctx).Infof("Transfer 50 from notary to recipient1") rpcerr = rpc.CallRPC(ctx, &boolResult, "testbed_invoke", &tktypes.PrivateContractInvoke{ From: notaryName, From 092899a3caf6156655cddaf3d29c5e414c87ffe1 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 16 Sep 2024 20:19:14 -0400 Subject: [PATCH 05/26] noto: add check for duplicate states Signed-off-by: Andrew Richardson --- domains/noto/internal/msgs/en_errors.go | 1 + domains/noto/internal/noto/noto.go | 19 ++++++++++++------- domains/noto/internal/noto/states.go | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/domains/noto/internal/msgs/en_errors.go b/domains/noto/internal/msgs/en_errors.go index 137d1615f..18572b924 100644 --- a/domains/noto/internal/msgs/en_errors.go +++ b/domains/noto/internal/msgs/en_errors.go @@ -59,4 +59,5 @@ var ( MsgSignatureDoesNotMatch = ffe("PD200017", "Signature for '%s' did not match: expected=%s actual=%s") MsgStateWrongOwner = ffe("PD200018", "State '%s' is not owned by '%s'") MsgUnrecognizedEndorsement = ffe("PD200019", "Unrecognized endorsement request: %s") + MsgDuplicateStateInList = ffe("PD200020", "Duplicate state in list %s[%d] (%s)") ) diff --git a/domains/noto/internal/noto/noto.go b/domains/noto/internal/noto/noto.go index bd2d2f278..ae9610eb0 100644 --- a/domains/noto/internal/noto/noto.go +++ b/domains/noto/internal/noto/noto.go @@ -289,19 +289,24 @@ func (n *Noto) recoverSignature(ctx context.Context, payload ethtypes.HexBytes0x func (n *Noto) parseCoinList(ctx context.Context, label string, states []*pb.EndorsableState) ([]*types.NotoCoin, []*pb.StateRef, *big.Int, error) { var err error + statesUsed := make(map[string]bool) coins := make([]*types.NotoCoin, len(states)) refs := make([]*pb.StateRef, len(states)) total := big.NewInt(0) - for i, input := range states { - if input.SchemaId != n.coinSchema.Id { - return nil, nil, nil, i18n.NewError(ctx, msgs.MsgUnknownSchema, input.SchemaId) + for i, state := range states { + if state.SchemaId != n.coinSchema.Id { + return nil, nil, nil, i18n.NewError(ctx, msgs.MsgUnknownSchema, state.SchemaId) + } + if statesUsed[state.Id] { + return nil, nil, nil, i18n.NewError(ctx, msgs.MsgDuplicateStateInList, label, i, state.Id) } - if coins[i], err = n.unmarshalCoin(input.StateDataJson); err != nil { - return nil, nil, nil, i18n.NewError(ctx, msgs.MsgInvalidListInput, label, i, input.Id, err) + statesUsed[state.Id] = true + if coins[i], err = n.unmarshalCoin(state.StateDataJson); err != nil { + return nil, nil, nil, i18n.NewError(ctx, msgs.MsgInvalidListInput, label, i, state.Id, err) } refs[i] = &pb.StateRef{ - SchemaId: input.SchemaId, - Id: input.Id, + SchemaId: state.SchemaId, + Id: state.Id, } total = total.Add(total, coins[i].Amount.BigInt()) } diff --git a/domains/noto/internal/noto/states.go b/domains/noto/internal/noto/states.go index 483d0c6b7..37bf37ec2 100644 --- a/domains/noto/internal/noto/states.go +++ b/domains/noto/internal/noto/states.go @@ -89,6 +89,7 @@ func (n *Noto) prepareInputs(ctx context.Context, owner ethtypes.Address0xHex, a stateRefs := []*pb.StateRef{} coins := []*types.NotoCoin{} for { + // TODO: make this configurable queryBuilder := query.NewQueryBuilder(). Limit(10). Sort(".created"). From 8c6d2427b0027477c7d36b8a8724c3db8fa6ec8e Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 10:42:46 -0400 Subject: [PATCH 06/26] Add back missed test coverage Signed-off-by: Andrew Richardson --- .../statestore/domain_context_test.go | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/core/go/internal/statestore/domain_context_test.go b/core/go/internal/statestore/domain_context_test.go index ee3011e1a..2c19b99a0 100644 --- a/core/go/internal/statestore/domain_context_test.go +++ b/core/go/internal/statestore/domain_context_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "testing" + "time" "github.com/google/uuid" "github.com/hyperledger/firefly-signer/pkg/abi" @@ -65,6 +66,52 @@ func parseFakeCoin(t *testing.T, s *State) *FakeCoin { return &c } +func TestStateFlushAsync(t *testing.T) { + + _, ss, done := newDBTestStateStore(t) + defer done() + + flushed := make(chan bool) + err := ss.RunInDomainContext("domain1", func(ctx context.Context, dsi DomainStateInterface) error { + return dsi.Flush(func(ctx context.Context, dsi DomainStateInterface) error { + flushed <- true + return nil + }) + }) + require.NoError(t, err) + + select { + case <-flushed: + case <-time.After(5 * time.Second): + assert.Fail(t, "timed out") + } + +} + +func TestUpsertSchemaAndStates(t *testing.T) { + + _, ss, done := newDBTestStateStore(t) + defer done() + + schemas, err := ss.EnsureABISchemas(context.Background(), "domain1", []*abi.Parameter{testABIParam(t, fakeCoinABI)}) + require.NoError(t, err) + require.Len(t, schemas, 1) + schemaID := schemas[0].IDString() + + err = ss.RunInDomainContext("domain1", func(ctx context.Context, dsi DomainStateInterface) error { + states, err := dsi.UpsertStates(nil, []*StateUpsert{ + { + SchemaID: schemaID, + Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 100, "owner": "0x1eDfD974fE6828dE81a1a762df680111870B7cDD", "salt": "%s"}`, tktypes.RandHex(32))), + }, + }) + require.NoError(t, err) + assert.Len(t, states, 1) + return nil + }) + require.NoError(t, err) + +} func TestStateContextMintSpendMint(t *testing.T) { _, ss, done := newDBTestStateStore(t) From 9510188dac7c6685902ffee57e287c3855cecd88 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 16 Sep 2024 17:51:24 -0400 Subject: [PATCH 07/26] Remove schema persistence from state writer Schemas are currently inserted only from the Paladin config on startup. Dynamically inserting schemas can be revisited if needed in the future, but for now it's added complexity that isn't actually useful. Signed-off-by: Andrew Richardson --- .../go/internal/statestore/abi_schema_test.go | 4 +-- core/go/internal/statestore/schema.go | 28 +++++++++++-------- .../internal/statestore/state_status_test.go | 2 +- core/go/internal/statestore/state_writer.go | 24 ++-------------- core/go/internal/statestore/statestore_rpc.go | 15 ---------- .../statestore/statestore_rpc_test.go | 20 +++++++------ 6 files changed, 35 insertions(+), 58 deletions(-) diff --git a/core/go/internal/statestore/abi_schema_test.go b/core/go/internal/statestore/abi_schema_test.go index a7130cc83..837e284ec 100644 --- a/core/go/internal/statestore/abi_schema_test.go +++ b/core/go/internal/statestore/abi_schema_test.go @@ -101,7 +101,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { cacheKey := "domain1/0xcf41493c8bb9652d1483ee6cb5122efbec6fbdf67cc27363ba5b030b59244cad" assert.Equal(t, cacheKey, schemaCacheKey(as.Persisted().DomainID, as.Persisted().ID)) - err = ss.PersistSchema(ctx, as) + err = ss.persistSchemas([]*SchemaPersisted{as.SchemaPersisted}) require.NoError(t, err) schemaID := as.Persisted().ID.String() @@ -157,7 +157,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { }`, string(state1.Data)) // Second should succeed, but not do anything - err = ss.PersistSchema(ctx, as) + err = ss.persistSchemas([]*SchemaPersisted{as.SchemaPersisted}) require.NoError(t, err) schemaID = as.IDString() diff --git a/core/go/internal/statestore/schema.go b/core/go/internal/statestore/schema.go index 52cca7471..0aa0b3039 100644 --- a/core/go/internal/statestore/schema.go +++ b/core/go/internal/statestore/schema.go @@ -23,6 +23,7 @@ import ( "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/core/internal/filters" "github.com/kaleido-io/paladin/core/internal/msgs" + "gorm.io/gorm/clause" "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" ) @@ -83,11 +84,18 @@ func schemaCacheKey(domainID string, id tktypes.Bytes32) string { return domainID + "/" + id.String() } -func (ss *stateStore) PersistSchema(ctx context.Context, s Schema) error { - op := ss.writer.newWriteOp(s.Persisted().DomainID) - op.schemas = []*SchemaPersisted{s.Persisted()} - ss.writer.queue(ctx, op) - return op.flush(ctx) +func (ss *stateStore) persistSchemas(schemas []*SchemaPersisted) error { + return ss.p.DB(). + Table("schemas"). + Clauses(clause.OnConflict{ + Columns: []clause.Column{ + {Name: "domain_id"}, + {Name: "id"}, + }, + DoNothing: true, // immutable + }). + Create(schemas). + Error } func (ss *stateStore) GetSchema(ctx context.Context, domainID, schemaID string, failNotFound bool) (Schema, error) { @@ -156,6 +164,9 @@ func (ss *stateStore) ListSchemas(ctx context.Context, domainID string) (results } func (ss *stateStore) EnsureABISchemas(ctx context.Context, domainID string, defs []*abi.Parameter) ([]Schema, error) { + if len(defs) == 0 { + return nil, nil + } // Validate all the schemas prepared := make([]Schema, len(defs)) @@ -169,10 +180,5 @@ func (ss *stateStore) EnsureABISchemas(ctx context.Context, domainID string, def toFlush[i] = s.SchemaPersisted } - op := ss.writer.newWriteOp(domainID) - op.schemas = toFlush - ss.writer.queue(ctx, op) - err := op.flush(ctx) - - return prepared, err + return prepared, ss.persistSchemas(toFlush) } diff --git a/core/go/internal/statestore/state_status_test.go b/core/go/internal/statestore/state_status_test.go index c7a9e7b09..c1264a498 100644 --- a/core/go/internal/statestore/state_status_test.go +++ b/core/go/internal/statestore/state_status_test.go @@ -84,7 +84,7 @@ func TestStateLockingQuery(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, widgetABI)) require.NoError(t, err) - err = ss.PersistSchema(ctx, schema) + err = ss.persistSchemas([]*SchemaPersisted{schema.SchemaPersisted}) require.NoError(t, err) schemaID := schema.IDString() diff --git a/core/go/internal/statestore/state_writer.go b/core/go/internal/statestore/state_writer.go index 7f3cb837d..120d45326 100644 --- a/core/go/internal/statestore/state_writer.go +++ b/core/go/internal/statestore/state_writer.go @@ -44,7 +44,6 @@ type writeOperation struct { stateSpends []*StateSpend stateLocks []*StateLock transactionLockDeletes []uuid.UUID - schemas []*SchemaPersisted } type stateWriter struct { @@ -189,7 +188,6 @@ func (sw *stateWriter) worker(i int) { func (sw *stateWriter) runBatch(ctx context.Context, b *stateWriterBatch) { // Build lists of things to insert (we are insert only) - var schemas []*SchemaPersisted var states []*State var labels []*StateLabel var int64Labels []*StateInt64Label @@ -198,9 +196,6 @@ func (sw *stateWriter) runBatch(ctx context.Context, b *stateWriterBatch) { var stateLocks []*StateLock var transactionLockDeletes []uuid.UUID for _, op := range b.ops { - if len(op.schemas) > 0 { - schemas = append(schemas, op.schemas...) - } for _, s := range op.states { states = append(states, s.State) labels = append(labels, s.State.Labels...) @@ -219,24 +214,11 @@ func (sw *stateWriter) runBatch(ctx context.Context, b *stateWriterBatch) { transactionLockDeletes = append(transactionLockDeletes, op.transactionLockDeletes...) } } - log.L(ctx).Debugf("Writing state batch schemas=%d states=%d confirms=%d spends=%d locks=%d seqLockDeletes=%d labels=%d int64Labels=%d", - len(schemas), len(states), len(stateConfirms), len(stateSpends), len(stateLocks), len(transactionLockDeletes), len(labels), len(int64Labels)) + log.L(ctx).Debugf("Writing state batch states=%d confirms=%d spends=%d locks=%d seqLockDeletes=%d labels=%d int64Labels=%d", + len(states), len(stateConfirms), len(stateSpends), len(stateLocks), len(transactionLockDeletes), len(labels), len(int64Labels)) err := sw.ss.p.DB().Transaction(func(tx *gorm.DB) (err error) { - if len(schemas) > 0 { - err = tx. - Table("schemas"). - Clauses(clause.OnConflict{ - Columns: []clause.Column{ - {Name: "domain_id"}, - {Name: "id"}, - }, - DoNothing: true, // immutable - }). - Create(schemas). - Error - } - if err == nil && len(states) > 0 { + if len(states) > 0 { err = tx. Table("states"). Clauses(clause.OnConflict{ diff --git a/core/go/internal/statestore/statestore_rpc.go b/core/go/internal/statestore/statestore_rpc.go index 512f5ffb3..5ebdfe531 100644 --- a/core/go/internal/statestore/statestore_rpc.go +++ b/core/go/internal/statestore/statestore_rpc.go @@ -19,7 +19,6 @@ package statestore import ( "context" - "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/kaleido-io/paladin/core/internal/rpcserver" "github.com/kaleido-io/paladin/toolkit/pkg/query" "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" @@ -31,25 +30,11 @@ func (ss *stateStore) RPCModule() *rpcserver.RPCModule { func (ss *stateStore) initRPC() { ss.rpcModule = rpcserver.NewRPCModule("pstate"). - Add("pstate_storeABISchema", ss.rpcStoreABISchema()). Add("pstate_listSchemas", ss.rpcListSchema()). Add("pstate_storeState", ss.rpcStoreState()). Add("pstate_queryStates", ss.rpcQuery()) } -func (ss *stateStore) rpcStoreABISchema() rpcserver.RPCHandler { - return rpcserver.RPCMethod2(func(ctx context.Context, - domain string, - abiParam abi.Parameter, - ) (Schema, error) { - s, err := newABISchema(ctx, domain, &abiParam) - if err == nil { - err = ss.PersistSchema(ctx, s) - } - return s, err - }) -} - func (ss *stateStore) rpcListSchema() rpcserver.RPCHandler { return rpcserver.RPCMethod1(func(ctx context.Context, domain string, diff --git a/core/go/internal/statestore/statestore_rpc_test.go b/core/go/internal/statestore/statestore_rpc_test.go index 57f0be768..77db3adab 100644 --- a/core/go/internal/statestore/statestore_rpc_test.go +++ b/core/go/internal/statestore/statestore_rpc_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/go-resty/resty/v2" + "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/hyperledger/firefly-signer/pkg/rpcbackend" "github.com/kaleido-io/paladin/core/internal/httpserver" "github.com/kaleido-io/paladin/core/internal/rpcserver" @@ -32,7 +33,7 @@ import ( "github.com/stretchr/testify/require" ) -func newTestRPCServer(t *testing.T) (context.Context, rpcbackend.Backend, func()) { +func newTestRPCServer(t *testing.T) (context.Context, *stateStore, rpcbackend.Backend, func()) { ctx, ss, ssDone := newDBTestStateStore(t) s, err := rpcserver.NewRPCServer(ctx, &rpcserver.Config{ @@ -49,7 +50,7 @@ func newTestRPCServer(t *testing.T) (context.Context, rpcbackend.Backend, func() c := rpcbackend.NewRPCClient(resty.New().SetBaseURL(fmt.Sprintf("http://%s", s.HTTPAddr()))) - return ctx, c, func() { s.Stop(); ssDone() } + return ctx, ss, c, func() { s.Stop(); ssDone() } } @@ -61,16 +62,19 @@ func jsonTestLog(t *testing.T, desc string, f interface{}) { func TestRPC(t *testing.T) { - ctx, c, done := newTestRPCServer(t) + ctx, ss, c, done := newTestRPCServer(t) defer done() - var schema tktypes.RawJSON - rpcErr := c.CallRPC(ctx, &schema, "pstate_storeABISchema", "domain1", tktypes.RawJSON(widgetABI)) - jsonTestLog(t, "pstate_storeABISchema", schema) - assert.Nil(t, rpcErr) + var abiParam abi.Parameter + err := json.Unmarshal([]byte(widgetABI), &abiParam) + assert.NoError(t, err) + schema, err := newABISchema(ctx, "domain1", &abiParam) + assert.NoError(t, err) + err = ss.persistSchemas([]*SchemaPersisted{schema.SchemaPersisted}) + assert.NoError(t, err) var schemas []*SchemaPersisted - rpcErr = c.CallRPC(ctx, &schemas, "pstate_listSchemas", "domain1") + rpcErr := c.CallRPC(ctx, &schemas, "pstate_listSchemas", "domain1") jsonTestLog(t, "pstate_listSchemas", schemas) assert.Nil(t, rpcErr) assert.Len(t, schemas, 1) From ffc883b3a776eae0ed6b561bef59fb3a8fa13f3d Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Mon, 16 Sep 2024 22:31:50 -0400 Subject: [PATCH 08/26] Store domain instance address with each state Signed-off-by: Andrew Richardson --- .../000003_create_states_table.up.sql | 11 ++++---- .../sqlite/000003_create_states_table.up.sql | 11 ++++---- core/go/internal/domainmgr/domain.go | 2 +- core/go/internal/domainmgr/domain_test.go | 13 +++++---- .../domainmgr/private_smart_contract.go | 2 +- .../domainmgr/private_smart_contract_test.go | 12 ++++---- .../go/internal/statestore/abi_schema_test.go | 6 ++-- core/go/internal/statestore/domain_context.go | 6 ++-- .../statestore/domain_context_test.go | 18 ++++++------ core/go/internal/statestore/state.go | 28 ++++++++++--------- .../internal/statestore/state_status_test.go | 2 +- core/go/internal/statestore/state_test.go | 6 ++-- core/go/internal/statestore/statestore_rpc.go | 5 ++-- .../statestore/statestore_rpc_test.go | 2 +- toolkit/proto/protos/from_domain.proto | 5 ++-- 15 files changed, 70 insertions(+), 59 deletions(-) diff --git a/core/go/db/migrations/postgres/000003_create_states_table.up.sql b/core/go/db/migrations/postgres/000003_create_states_table.up.sql index d0cbafc34..ed43a1373 100644 --- a/core/go/db/migrations/postgres/000003_create_states_table.up.sql +++ b/core/go/db/migrations/postgres/000003_create_states_table.up.sql @@ -1,11 +1,12 @@ BEGIN; CREATE TABLE states ( - "id" TEXT NOT NULL, - "created_at" BIGINT NOT NULL, - "domain_id" TEXT, - "schema" TEXT, - "data" TEXT, + "id" TEXT NOT NULL, + "created_at" BIGINT NOT NULL, + "domain_id" TEXT, + "schema" TEXT, + "domain_address" TEXT, + "data" TEXT, PRIMARY KEY ("id"), FOREIGN KEY ("domain_id", "schema") REFERENCES schemas ("domain_id", "id") ON DELETE CASCADE ); diff --git a/core/go/db/migrations/sqlite/000003_create_states_table.up.sql b/core/go/db/migrations/sqlite/000003_create_states_table.up.sql index 4fc1bb4b7..80068749a 100644 --- a/core/go/db/migrations/sqlite/000003_create_states_table.up.sql +++ b/core/go/db/migrations/sqlite/000003_create_states_table.up.sql @@ -1,9 +1,10 @@ CREATE TABLE states ( - "id" VARCHAR NOT NULL, - "created_at" BIGINT NOT NULL, - "domain_id" VARCHAR, - "schema" VARCHAR, - "data" VARCHAR, + "id" VARCHAR NOT NULL, + "created_at" BIGINT NOT NULL, + "domain_id" VARCHAR, + "schema" VARCHAR, + "domain_address" VARCHAR, + "data" VARCHAR, PRIMARY KEY ("id"), FOREIGN KEY ("domain_id", "schema") REFERENCES schemas ("domain_id", "id") ON DELETE CASCADE ); diff --git a/core/go/internal/domainmgr/domain.go b/core/go/internal/domainmgr/domain.go index 8cc0da45b..02ffb8ef6 100644 --- a/core/go/internal/domainmgr/domain.go +++ b/core/go/internal/domainmgr/domain.go @@ -199,7 +199,7 @@ func (d *domain) FindAvailableStates(ctx context.Context, req *prototk.FindAvail var states []*statestore.State err = d.dm.stateStore.RunInDomainContext(d.name, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { - states, err = dsi.FindAvailableStates(req.SchemaId, &query) + states, err = dsi.FindAvailableStates(req.DomainAddress, req.SchemaId, &query) return err }) if err != nil { diff --git a/core/go/internal/domainmgr/domain_test.go b/core/go/internal/domainmgr/domain_test.go index db8923d3f..23fc92a7b 100644 --- a/core/go/internal/domainmgr/domain_test.go +++ b/core/go/internal/domainmgr/domain_test.go @@ -323,13 +323,14 @@ func TestDomainFindAvailableStatesBadQuery(t *testing.T) { func TestDomainFindAvailableStatesFail(t *testing.T) { ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), func(mc *mockComponents) { mc.stateStore.On("EnsureABISchemas", mock.Anything, "test1", mock.Anything).Return([]statestore.Schema{}, nil) - mc.domainStateInterface.On("FindAvailableStates", "12345", mock.Anything).Return(nil, fmt.Errorf("pop")) + mc.domainStateInterface.On("FindAvailableStates", "0x1234", "12345", mock.Anything).Return(nil, fmt.Errorf("pop")) }) defer done() assert.Nil(t, tp.d.initError.Load()) _, err := tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - SchemaId: "12345", - QueryJson: `{}`, + DomainAddress: "0x1234", + SchemaId: "12345", + QueryJson: `{}`, }) assert.Regexp(t, "pop", err) } @@ -368,7 +369,8 @@ func TestDomainFindAvailableStatesOK(t *testing.T) { // Filter match states, err := tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - SchemaId: tp.stateSchemas[0].Id, + DomainAddress: "0x1234", + SchemaId: tp.stateSchemas[0].Id, QueryJson: `{ "eq": [ { "field": "owner", "value": "` + state1.Owner.String() + `" } @@ -380,7 +382,8 @@ func TestDomainFindAvailableStatesOK(t *testing.T) { // Filter miss states, err = tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - SchemaId: tp.stateSchemas[0].Id, + DomainAddress: "0x1234", + SchemaId: tp.stateSchemas[0].Id, QueryJson: `{ "eq": [ { "field": "owner", "value": "` + tktypes.EthAddress(tktypes.RandBytes(20)).String() + `" } diff --git a/core/go/internal/domainmgr/private_smart_contract.go b/core/go/internal/domainmgr/private_smart_contract.go index 119d4ece1..bfaf56678 100644 --- a/core/go/internal/domainmgr/private_smart_contract.go +++ b/core/go/internal/domainmgr/private_smart_contract.go @@ -418,7 +418,7 @@ func (dc *domainContract) loadStates(ctx context.Context, refs []*prototk.StateR statesByID := make(map[tktypes.Bytes32]*statestore.State) err := dc.dm.stateStore.RunInDomainContext(dc.d.name, func(ctx context.Context, dsi statestore.DomainStateInterface) error { for schemaID, stateIDs := range rawIDsBySchema { - statesForSchema, err := dsi.FindAvailableStates(schemaID, &query.QueryJSON{ + statesForSchema, err := dsi.FindAvailableStates(dc.info.Address.String(), schemaID, &query.QueryJSON{ Statements: query.Statements{ Ops: query.Ops{ In: []*query.OpMultiVal{ diff --git a/core/go/internal/domainmgr/private_smart_contract_test.go b/core/go/internal/domainmgr/private_smart_contract_test.go index 3192d8fe5..63aed1e2c 100644 --- a/core/go/internal/domainmgr/private_smart_contract_test.go +++ b/core/go/internal/domainmgr/private_smart_contract_test.go @@ -382,7 +382,8 @@ func TestFullTransactionRealDBOK(t *testing.T) { require.NoError(t, err) stateRes, err := domain.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, + DomainAddress: "0x1234", + SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, QueryJson: `{ "or": [ { @@ -399,8 +400,9 @@ func TestFullTransactionRealDBOK(t *testing.T) { require.NoError(t, err) stillAvailable, err := domain.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, - QueryJson: `{}`, + DomainAddress: "0x1234", + SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, + QueryJson: `{}`, }) require.NoError(t, err) assert.Len(t, stillAvailable.States, 3) @@ -730,7 +732,7 @@ func TestPrepareTransactionBadData(t *testing.T) { func TestLoadStatesError(t *testing.T) { ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), mockSchemas(), mockBlockHeight, func(mc *mockComponents) { - mc.domainStateInterface.On("FindAvailableStates", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) + mc.domainStateInterface.On("FindAvailableStates", mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) }) defer done() @@ -745,7 +747,7 @@ func TestLoadStatesError(t *testing.T) { func TestLoadStatesNotFound(t *testing.T) { ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), mockSchemas(), mockBlockHeight, func(mc *mockComponents) { - mc.domainStateInterface.On("FindAvailableStates", mock.Anything, mock.Anything).Return([]*statestore.State{}, nil) + mc.domainStateInterface.On("FindAvailableStates", mock.Anything, mock.Anything, mock.Anything).Return([]*statestore.State{}, nil) }) defer done() diff --git a/core/go/internal/statestore/abi_schema_test.go b/core/go/internal/statestore/abi_schema_test.go index 837e284ec..d210dde5d 100644 --- a/core/go/internal/statestore/abi_schema_test.go +++ b/core/go/internal/statestore/abi_schema_test.go @@ -200,7 +200,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err := ss.FindStates(ctx, as.Persisted().DomainID, schemaID, query, "all") + states, err := ss.FindStates(ctx, as.Persisted().DomainID, "0x1234", schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 1) @@ -211,7 +211,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err = ss.FindStates(ctx, as.Persisted().DomainID, schemaID, query, "all") + states, err = ss.FindStates(ctx, as.Persisted().DomainID, "0x1234", schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 0) @@ -222,7 +222,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err = ss.FindStates(ctx, as.Persisted().DomainID, schemaID, query, "all") + states, err = ss.FindStates(ctx, as.Persisted().DomainID, "0x1234", schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 0) } diff --git a/core/go/internal/statestore/domain_context.go b/core/go/internal/statestore/domain_context.go index 874600329..f307ec7bf 100644 --- a/core/go/internal/statestore/domain_context.go +++ b/core/go/internal/statestore/domain_context.go @@ -52,7 +52,7 @@ type DomainStateInterface interface { // 2) We deliberately return states that are locked to a transaction (but not spent yet) - which means the // result of the any assemble that uses those states, will be a transaction that must // be on the same transaction where those states are locked. - FindAvailableStates(schemaID string, query *query.QueryJSON) (s []*State, err error) + FindAvailableStates(domainAddress, schemaID string, query *query.QueryJSON) (s []*State, err error) // MarkStatesSpending writes a lock record so the state is now locked for spending, and // thus subsequent calls to FindAvailableStates will not return these states. @@ -291,7 +291,7 @@ func (dc *domainContext) mergeInMemoryMatches(schema Schema, states []*State, ex } -func (dc *domainContext) FindAvailableStates(schemaID string, query *query.QueryJSON) (s []*State, err error) { +func (dc *domainContext) FindAvailableStates(domainAddress, schemaID string, query *query.QueryJSON) (s []*State, err error) { // Build a list of excluded states excluded, err := dc.getUnFlushedSpending() @@ -300,7 +300,7 @@ func (dc *domainContext) FindAvailableStates(schemaID string, query *query.Query } // Run the query against the DB - schema, states, err := dc.ss.findStates(dc.ctx, dc.domainID, schemaID, query, StateStatusAvailable, excluded...) + schema, states, err := dc.ss.findStates(dc.ctx, dc.domainID, domainAddress, schemaID, query, StateStatusAvailable, excluded...) if err != nil { return nil, err } diff --git a/core/go/internal/statestore/domain_context_test.go b/core/go/internal/statestore/domain_context_test.go index 2c19b99a0..229c08e2c 100644 --- a/core/go/internal/statestore/domain_context_test.go +++ b/core/go/internal/statestore/domain_context_test.go @@ -149,7 +149,7 @@ func TestStateContextMintSpendMint(t *testing.T) { // Query the states, and notice we find the ones that are still in the process of creating // even though they've not yet been written to the DB - states, err := dsi.FindAvailableStates(schemaID, toQuery(t, `{ + states, err := dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ "sort": [ "amount" ] }`)) require.NoError(t, err) @@ -183,7 +183,7 @@ func TestStateContextMintSpendMint(t *testing.T) { } // Query the states on the first address - states, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{ + states, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ "sort": [ "-amount" ], "eq": [{"field": "owner", "value": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180"}] }`)) @@ -193,7 +193,7 @@ func TestStateContextMintSpendMint(t *testing.T) { assert.Equal(t, int64(35), parseFakeCoin(t, states[1]).Amount.Int64()) // Query the states on the other address - states, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{ + states, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ "sort": [ "-amount" ], "eq": [{"field": "owner", "value": "0x615dD09124271D8008225054d85Ffe720E7a447A"}] }`)) @@ -208,7 +208,7 @@ func TestStateContextMintSpendMint(t *testing.T) { err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { // Check the DB persisted state is what we expect - states, err := dsi.FindAvailableStates(schemaID, toQuery(t, `{ + states, err := dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ "sort": [ "owner", "amount" ] }`)) require.NoError(t, err) @@ -236,7 +236,7 @@ func TestStateContextMintSpendMint(t *testing.T) { assert.Len(t, tx3states, 2) // Now check that we merge the DB and in-memory state - states, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{ + states, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ "sort": [ "owner", "amount" ] }`)) require.NoError(t, err) @@ -247,7 +247,7 @@ func TestStateContextMintSpendMint(t *testing.T) { assert.Equal(t, int64(100), parseFakeCoin(t, states[3]).Amount.Int64()) // Check the limit works too across this - states, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{ + states, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ "limit": 1, "sort": [ "owner", "amount" ] }`)) @@ -270,7 +270,7 @@ func TestStateContextMintSpendMint(t *testing.T) { err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { // Confirm - states, err := dsi.FindAvailableStates(schemaID, toQuery(t, `{}`)) + states, err := dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{}`)) require.NoError(t, err) assert.Empty(t, states) @@ -323,7 +323,7 @@ func TestDSIFlushErrorCapture(t *testing.T) { dc := dsi.(*domainContext) fakeFlushError(dc) - _, err = dsi.FindAvailableStates("", nil) + _, err = dsi.FindAvailableStates("", "", nil) assert.Regexp(t, "pop", err) fakeFlushError(dc) @@ -528,7 +528,7 @@ func TestDSIFindBadQueryAndInsert(t *testing.T) { assert.Equal(t, "type=FakeCoin(bytes32 salt,address owner,uint256 amount),labels=[owner,amount]", schemas[0].Signature()) err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { - _, err = dsi.FindAvailableStates(schemaID, toQuery(t, + _, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{"sort":["wrong"]}`)) assert.Regexp(t, "PD010700", err) diff --git a/core/go/internal/statestore/state.go b/core/go/internal/statestore/state.go index 60bef1ccd..30303ae22 100644 --- a/core/go/internal/statestore/state.go +++ b/core/go/internal/statestore/state.go @@ -30,16 +30,17 @@ import ( ) type State struct { - ID tktypes.Bytes32 `json:"id" gorm:"primaryKey"` - CreatedAt tktypes.Timestamp `json:"created" gorm:"autoCreateTime:nano"` - DomainID string `json:"domain"` - Schema tktypes.Bytes32 `json:"schema"` - Data tktypes.RawJSON `json:"data"` - Labels []*StateLabel `json:"-" gorm:"foreignKey:state;references:id;"` - Int64Labels []*StateInt64Label `json:"-" gorm:"foreignKey:state;references:id;"` - Confirmed *StateConfirm `json:"confirmed,omitempty" gorm:"foreignKey:state;references:id;"` - Spent *StateSpend `json:"spent,omitempty" gorm:"foreignKey:state;references:id;"` - Locked *StateLock `json:"locked,omitempty" gorm:"foreignKey:state;references:id;"` + ID tktypes.Bytes32 `json:"id" gorm:"primaryKey"` + CreatedAt tktypes.Timestamp `json:"created" gorm:"autoCreateTime:nano"` + DomainID string `json:"domain"` + DomainAddress string `json:"domainAddress"` + Schema tktypes.Bytes32 `json:"schema"` + Data tktypes.RawJSON `json:"data"` + Labels []*StateLabel `json:"-" gorm:"foreignKey:state;references:id;"` + Int64Labels []*StateInt64Label `json:"-" gorm:"foreignKey:state;references:id;"` + Confirmed *StateConfirm `json:"confirmed,omitempty" gorm:"foreignKey:state;references:id;"` + Spent *StateSpend `json:"spent,omitempty" gorm:"foreignKey:state;references:id;"` + Locked *StateLock `json:"locked,omitempty" gorm:"foreignKey:state;references:id;"` } type StateUpsert struct { @@ -156,12 +157,12 @@ func (ss *stateStore) labelSetFor(schema Schema) *trackingLabelSet { return &tls } -func (ss *stateStore) FindStates(ctx context.Context, domainID, schemaID string, query *query.QueryJSON, status StateStatusQualifier) (s []*State, err error) { - _, s, err = ss.findStates(ctx, domainID, schemaID, query, status) +func (ss *stateStore) FindStates(ctx context.Context, domainID, domainAddress, schemaID string, query *query.QueryJSON, status StateStatusQualifier) (s []*State, err error) { + _, s, err = ss.findStates(ctx, domainID, domainAddress, schemaID, query, status) return s, err } -func (ss *stateStore) findStates(ctx context.Context, domainID, schemaID string, jq *query.QueryJSON, status StateStatusQualifier, excluded ...*idOnly) (schema Schema, s []*State, err error) { +func (ss *stateStore) findStates(ctx context.Context, domainID, domainAddress, schemaID string, jq *query.QueryJSON, status StateStatusQualifier, excluded ...*idOnly) (schema Schema, s []*State, err error) { if len(jq.Sort) == 0 { jq.Sort = []string{".created"} } @@ -193,6 +194,7 @@ func (ss *stateStore) findStates(ctx context.Context, domainID, schemaID string, Joins("Spent", db.Select("transaction")). Joins("Locked", db.Select("transaction")). Where("domain_id = ?", domainID). + // Where("domain_address = ?", domainAddress). Where("schema = ?", schema.Persisted().ID) if len(excluded) > 0 { diff --git a/core/go/internal/statestore/state_status_test.go b/core/go/internal/statestore/state_status_test.go index c1264a498..682982441 100644 --- a/core/go/internal/statestore/state_status_test.go +++ b/core/go/internal/statestore/state_status_test.go @@ -97,7 +97,7 @@ func TestStateLockingQuery(t *testing.T) { }) checkQuery := func(query string, status StateStatusQualifier, expected ...int) { - states, err := ss.FindStates(ctx, "domain1", schemaID, toQuery(t, query), status) + states, err := ss.FindStates(ctx, "domain1", "0x1234", schemaID, toQuery(t, query), status) require.NoError(t, err) assert.Len(t, states, len(expected)) for _, wIndex := range expected { diff --git a/core/go/internal/statestore/state_test.go b/core/go/internal/statestore/state_test.go index 7e6a50f95..19b8d9e93 100644 --- a/core/go/internal/statestore/state_test.go +++ b/core/go/internal/statestore/state_test.go @@ -100,7 +100,7 @@ func TestFindStatesMissingSchema(t *testing.T) { db.ExpectQuery("SELECT").WillReturnRows(db.NewRows([]string{})) - _, err := ss.FindStates(ctx, "domain1", tktypes.Bytes32Keccak(([]byte)("schema1")).String(), &query.QueryJSON{}, "all") + _, err := ss.FindStates(ctx, "domain1", "0x1234", tktypes.Bytes32Keccak(([]byte)("schema1")).String(), &query.QueryJSON{}, "all") assert.Regexp(t, "PD010106", err) } @@ -114,7 +114,7 @@ func TestFindStatesBadQuery(t *testing.T) { definition: &abi.Parameter{}, }) - _, err := ss.FindStates(ctx, "domain1", schemaID.String(), &query.QueryJSON{ + _, err := ss.FindStates(ctx, "domain1", "0x1234", schemaID.String(), &query.QueryJSON{ Statements: query.Statements{ Ops: query.Ops{ Equal: []*query.OpSingleVal{ @@ -140,7 +140,7 @@ func TestFindStatesFail(t *testing.T) { db.ExpectQuery("SELECT.*created_at").WillReturnError(fmt.Errorf("pop")) - _, err := ss.FindStates(ctx, "domain1", schemaID.String(), &query.QueryJSON{ + _, err := ss.FindStates(ctx, "domain1", "0x1234", schemaID.String(), &query.QueryJSON{ Statements: query.Statements{ Ops: query.Ops{ GreaterThan: []*query.OpSingleVal{ diff --git a/core/go/internal/statestore/statestore_rpc.go b/core/go/internal/statestore/statestore_rpc.go index 5ebdfe531..29adff531 100644 --- a/core/go/internal/statestore/statestore_rpc.go +++ b/core/go/internal/statestore/statestore_rpc.go @@ -59,12 +59,13 @@ func (ss *stateStore) rpcStoreState() rpcserver.RPCHandler { } func (ss *stateStore) rpcQuery() rpcserver.RPCHandler { - return rpcserver.RPCMethod4(func(ctx context.Context, + return rpcserver.RPCMethod5(func(ctx context.Context, domain string, + domainAddress string, schema string, query query.QueryJSON, status StateStatusQualifier, ) ([]*State, error) { - return ss.FindStates(ctx, domain, schema, &query, status) + return ss.FindStates(ctx, domain, domainAddress, schema, &query, status) }) } diff --git a/core/go/internal/statestore/statestore_rpc_test.go b/core/go/internal/statestore/statestore_rpc_test.go index 77db3adab..ed89673ed 100644 --- a/core/go/internal/statestore/statestore_rpc_test.go +++ b/core/go/internal/statestore/statestore_rpc_test.go @@ -95,7 +95,7 @@ func TestRPC(t *testing.T) { assert.Equal(t, "0x30e278bca8d876cdceb24520b0ebe736a64a9cb8019157f40fa5b03f083f824d", state.ID.String()) var states []*State - rpcErr = c.CallRPC(ctx, &states, "pstate_queryStates", "domain1", schemas[0].ID, tktypes.RawJSON(`{ + rpcErr = c.CallRPC(ctx, &states, "pstate_queryStates", "domain1", "0x1234", schemas[0].ID, tktypes.RawJSON(`{ "eq": [{ "field": "color", "value": "blue" diff --git a/toolkit/proto/protos/from_domain.proto b/toolkit/proto/protos/from_domain.proto index 99745f41f..46a887cc1 100644 --- a/toolkit/proto/protos/from_domain.proto +++ b/toolkit/proto/protos/from_domain.proto @@ -18,8 +18,9 @@ syntax = "proto3"; package github.com.kaleido_io.paladin.toolkit; message FindAvailableStatesRequest { - string schema_id = 1; // The ID of the schema - string query_json = 2; // The query specification in JSON + string domain_address = 1; // The address of the domain instance + string schema_id = 2; // The ID of the schema + string query_json = 3; // The query specification in JSON } message FindAvailableStatesResponse { From e09a017d5ab68648120dd34c8a8af324913db340 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 10:56:25 -0400 Subject: [PATCH 09/26] Scope state queries to a single domain address Signed-off-by: Andrew Richardson --- core/go/internal/domainmgr/domain_test.go | 6 +++--- .../domainmgr/private_smart_contract.go | 6 +++--- .../domainmgr/private_smart_contract_test.go | 17 +++++++++-------- core/go/internal/statestore/abi_schema_test.go | 2 +- core/go/internal/statestore/domain_context.go | 5 +++-- .../internal/statestore/domain_context_test.go | 14 +++++++------- core/go/internal/statestore/state.go | 5 +++-- .../go/internal/statestore/state_status_test.go | 2 +- core/go/internal/statestore/state_test.go | 4 ++-- core/go/internal/statestore/statestore_rpc.go | 5 +++-- .../internal/statestore/statestore_rpc_test.go | 2 +- .../ut_simdomain_notarized_token_test.go | 9 +++++---- domains/noto/internal/noto/e2e_noto_test.go | 12 ++++++------ domains/noto/internal/noto/handler_transfer.go | 2 +- domains/noto/internal/noto/noto.go | 4 ++-- domains/noto/internal/noto/states.go | 11 ++++++----- domains/noto/pkg/noto/noto.go | 2 +- .../paladin/pente/domain/PenteDomain.java | 9 ++++++++- domains/zeto/integration-test/e2e_test.go | 4 ++-- domains/zeto/internal/zeto/handler_transfer.go | 2 +- domains/zeto/internal/zeto/states.go | 11 ++++++----- domains/zeto/internal/zeto/zeto.go | 4 ++-- domains/zeto/pkg/zeto/zeto.go | 2 +- 23 files changed, 77 insertions(+), 63 deletions(-) diff --git a/core/go/internal/domainmgr/domain_test.go b/core/go/internal/domainmgr/domain_test.go index 23fc92a7b..5c9ea0218 100644 --- a/core/go/internal/domainmgr/domain_test.go +++ b/core/go/internal/domainmgr/domain_test.go @@ -335,7 +335,7 @@ func TestDomainFindAvailableStatesFail(t *testing.T) { assert.Regexp(t, "pop", err) } -func storeState(t *testing.T, dm *domainManager, tp *testPlugin, txID uuid.UUID, amount *ethtypes.HexInteger) *fakeState { +func storeState(t *testing.T, dm *domainManager, tp *testPlugin, domainAddress string, txID uuid.UUID, amount *ethtypes.HexInteger) *fakeState { state := &fakeState{ Salt: tktypes.Bytes32(tktypes.RandBytes(32)), Owner: tktypes.EthAddress(tktypes.RandBytes(20)), @@ -345,7 +345,7 @@ func storeState(t *testing.T, dm *domainManager, tp *testPlugin, txID uuid.UUID, require.NoError(t, err) err = dm.stateStore.RunInDomainContextFlush("test1", func(ctx context.Context, dsi statestore.DomainStateInterface) error { - newStates, err := dsi.UpsertStates(&txID, []*statestore.StateUpsert{ + newStates, err := dsi.UpsertStates(domainAddress, &txID, []*statestore.StateUpsert{ { SchemaID: tp.stateSchemas[0].Id, Data: stateJSON, @@ -365,7 +365,7 @@ func TestDomainFindAvailableStatesOK(t *testing.T) { assert.Nil(t, tp.d.initError.Load()) txID := uuid.New() - state1 := storeState(t, dm, tp, txID, ethtypes.NewHexIntegerU64(100000000)) + state1 := storeState(t, dm, tp, "0x1234", txID, ethtypes.NewHexIntegerU64(100000000)) // Filter match states, err := tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ diff --git a/core/go/internal/domainmgr/private_smart_contract.go b/core/go/internal/domainmgr/private_smart_contract.go index bfaf56678..882c76fcd 100644 --- a/core/go/internal/domainmgr/private_smart_contract.go +++ b/core/go/internal/domainmgr/private_smart_contract.go @@ -190,7 +190,7 @@ func (dc *domainContract) WritePotentialStates(ctx context.Context, tx *componen var states []*statestore.State if len(newStatesToWrite) > 0 { err := dc.dm.stateStore.RunInDomainContext(domain.name, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { - states, err = dsi.UpsertStates(&tx.ID, newStatesToWrite) + states, err = dsi.UpsertStates(dc.info.Address.String(), &tx.ID, newStatesToWrite) return err }) if err != nil { @@ -256,9 +256,9 @@ func (dc *domainContract) LockStates(ctx context.Context, tx *components.Private return dc.dm.stateStore.RunInDomainContext(dc.d.name, func(ctx context.Context, dsi statestore.DomainStateInterface) error { // Heavy lifting is all done for us by the state store - _, err := dsi.UpsertStates(&tx.ID, txLockedStateUpserts) + _, err := dsi.UpsertStates(dc.info.Address.String(), &tx.ID, txLockedStateUpserts) if err == nil && len(readStateUpserts) > 0 { - _, err = dsi.UpsertStates(nil, readStateUpserts) + _, err = dsi.UpsertStates(dc.info.Address.String(), nil, readStateUpserts) } return err }) diff --git a/core/go/internal/domainmgr/private_smart_contract_test.go b/core/go/internal/domainmgr/private_smart_contract_test.go index 63aed1e2c..6c752b8c0 100644 --- a/core/go/internal/domainmgr/private_smart_contract_test.go +++ b/core/go/internal/domainmgr/private_smart_contract_test.go @@ -310,10 +310,10 @@ func TestFullTransactionRealDBOK(t *testing.T) { psc, tx := doDomainInitTransactionOK(t, ctx, tp) domain := tp.d - state1 := storeState(t, dm, tp, tx.ID, ethtypes.NewHexInteger64(1111111)) - state2 := storeState(t, dm, tp, tx.ID, ethtypes.NewHexInteger64(2222222)) - state3 := storeState(t, dm, tp, tx.ID, ethtypes.NewHexInteger64(3333333)) - state4 := storeState(t, dm, tp, tx.ID, ethtypes.NewHexInteger64(4444444)) + state1 := storeState(t, dm, tp, psc.Address().String(), tx.ID, ethtypes.NewHexInteger64(1111111)) + state2 := storeState(t, dm, tp, psc.Address().String(), tx.ID, ethtypes.NewHexInteger64(2222222)) + state3 := storeState(t, dm, tp, psc.Address().String(), tx.ID, ethtypes.NewHexInteger64(3333333)) + state4 := storeState(t, dm, tp, psc.Address().String(), tx.ID, ethtypes.NewHexInteger64(4444444)) state5 := &fakeState{ Salt: tktypes.Bytes32(tktypes.RandBytes(32)), @@ -324,7 +324,8 @@ func TestFullTransactionRealDBOK(t *testing.T) { assert.Same(t, req.Transaction, tx.PreAssembly.TransactionSpecification) stateRes, err := domain.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - SchemaId: tp.stateSchemas[0].Id, + DomainAddress: req.Transaction.ContractAddress, + SchemaId: tp.stateSchemas[0].Id, QueryJson: `{ "or": [ { @@ -382,7 +383,7 @@ func TestFullTransactionRealDBOK(t *testing.T) { require.NoError(t, err) stateRes, err := domain.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: "0x1234", + DomainAddress: psc.Address().String(), SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, QueryJson: `{ "or": [ @@ -400,7 +401,7 @@ func TestFullTransactionRealDBOK(t *testing.T) { require.NoError(t, err) stillAvailable, err := domain.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: "0x1234", + DomainAddress: psc.Address().String(), SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, QueryJson: `{}`, }) @@ -584,7 +585,7 @@ func TestDomainWritePotentialStatesFail(t *testing.T) { schema.On("IDString").Return("schema1") schema.On("Signature").Return("schema1_signature") ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), mockSchemas(schema), mockBlockHeight, func(mc *mockComponents) { - mc.domainStateInterface.On("UpsertStates", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) + mc.domainStateInterface.On("UpsertStates", mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) }) defer done() diff --git a/core/go/internal/statestore/abi_schema_test.go b/core/go/internal/statestore/abi_schema_test.go index d210dde5d..18eb1f453 100644 --- a/core/go/internal/statestore/abi_schema_test.go +++ b/core/go/internal/statestore/abi_schema_test.go @@ -106,7 +106,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { schemaID := as.Persisted().ID.String() // Check it handles data - state1, err := ss.PersistState(ctx, "domain1", schemaID, tktypes.RawJSON(`{ + state1, err := ss.PersistState(ctx, "domain1", "0x1234", schemaID, tktypes.RawJSON(`{ "field1": "0x0123456789012345678901234567890123456789", "field2": "hello world", "field3": 42, diff --git a/core/go/internal/statestore/domain_context.go b/core/go/internal/statestore/domain_context.go index f307ec7bf..061f66629 100644 --- a/core/go/internal/statestore/domain_context.go +++ b/core/go/internal/statestore/domain_context.go @@ -70,7 +70,7 @@ type DomainStateInterface interface { // If a non-nil transaction ID is supplied, then the states are mark locked to the specified // transaction. They can then be locked-for-creation, locked-for-spending, for simply // locked for existence to avoid other transactions spending them. - UpsertStates(transactionID *uuid.UUID, states []*StateUpsert) (s []*State, err error) + UpsertStates(domainAddress string, transactionID *uuid.UUID, states []*StateUpsert) (s []*State, err error) // ResetTransaction queues up removal of all lock records for a given transaction // Note that the private data of the states themselves are not removed @@ -309,7 +309,7 @@ func (dc *domainContext) FindAvailableStates(domainAddress, schemaID string, que return dc.mergedUnFlushed(schema, states, query) } -func (dc *domainContext) UpsertStates(transactionID *uuid.UUID, stateUpserts []*StateUpsert) (states []*State, err error) { +func (dc *domainContext) UpsertStates(domainAddress string, transactionID *uuid.UUID, stateUpserts []*StateUpsert) (states []*State, err error) { states = make([]*State, len(stateUpserts)) withValues := make([]*StateWithLabels, len(stateUpserts)) @@ -324,6 +324,7 @@ func (dc *domainContext) UpsertStates(transactionID *uuid.UUID, stateUpserts []* return nil, err } states[i] = withValues[i].State + states[i].DomainAddress = domainAddress if transactionID != nil { states[i].Locked = &StateLock{ Transaction: *transactionID, diff --git a/core/go/internal/statestore/domain_context_test.go b/core/go/internal/statestore/domain_context_test.go index 229c08e2c..39d89e723 100644 --- a/core/go/internal/statestore/domain_context_test.go +++ b/core/go/internal/statestore/domain_context_test.go @@ -99,7 +99,7 @@ func TestUpsertSchemaAndStates(t *testing.T) { schemaID := schemas[0].IDString() err = ss.RunInDomainContext("domain1", func(ctx context.Context, dsi DomainStateInterface) error { - states, err := dsi.UpsertStates(nil, []*StateUpsert{ + states, err := dsi.UpsertStates("0x1234", nil, []*StateUpsert{ { SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 100, "owner": "0x1eDfD974fE6828dE81a1a762df680111870B7cDD", "salt": "%s"}`, tktypes.RandHex(32))), @@ -129,7 +129,7 @@ func TestStateContextMintSpendMint(t *testing.T) { err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { // Store some states - tx1states, err := dsi.UpsertStates(&transactionID, []*StateUpsert{ + tx1states, err := dsi.UpsertStates("0x1234", &transactionID, []*StateUpsert{ {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 100, "owner": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 10, "owner": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 75, "owner": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, @@ -172,7 +172,7 @@ func TestStateContextMintSpendMint(t *testing.T) { // Do a quick check on upsert semantics with un-flushed updates, to make sure the unflushed list doesn't dup tx2Salts := []string{tktypes.RandHex(32), tktypes.RandHex(32)} for dup := 0; dup < 2; dup++ { - tx2states, err := dsi.UpsertStates(&transactionID, []*StateUpsert{ + tx2states, err := dsi.UpsertStates("0x1234", &transactionID, []*StateUpsert{ {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 35, "owner": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180", "salt": "%s"}`, tx2Salts[0])), Creating: true}, {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 50, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tx2Salts[1])), Creating: true}, }) @@ -228,7 +228,7 @@ func TestStateContextMintSpendMint(t *testing.T) { states[0].ID.String(), // 50 }) require.NoError(t, err) - tx3states, err := dsi.UpsertStates(&transactionID, []*StateUpsert{ + tx3states, err := dsi.UpsertStates("0x1234", &transactionID, []*StateUpsert{ {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 30, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, }) @@ -333,7 +333,7 @@ func TestDSIFlushErrorCapture(t *testing.T) { assert.Regexp(t, "pop", err) fakeFlushError(dc) - _, err = dsi.UpsertStates(nil, nil) + _, err = dsi.UpsertStates("0x1234", nil, nil) assert.Regexp(t, "pop", err) fakeFlushError(dc) @@ -532,7 +532,7 @@ func TestDSIFindBadQueryAndInsert(t *testing.T) { `{"sort":["wrong"]}`)) assert.Regexp(t, "PD010700", err) - _, err = dsi.UpsertStates(nil, []*StateUpsert{ + _, err = dsi.UpsertStates("0x1234", nil, []*StateUpsert{ {SchemaID: schemaID, Data: tktypes.RawJSON(`"wrong"`)}, }) assert.Regexp(t, "FF22038", err) @@ -550,7 +550,7 @@ func TestDSIBadIDs(t *testing.T) { _ = ss.RunInDomainContext("domain1", func(ctx context.Context, dsi DomainStateInterface) error { - _, err := dsi.UpsertStates(nil, []*StateUpsert{ + _, err := dsi.UpsertStates("0x1234", nil, []*StateUpsert{ {SchemaID: "wrong"}, }) assert.Regexp(t, "PD020007", err) diff --git a/core/go/internal/statestore/state.go b/core/go/internal/statestore/state.go index 30303ae22..43a7676f2 100644 --- a/core/go/internal/statestore/state.go +++ b/core/go/internal/statestore/state.go @@ -77,7 +77,7 @@ func (s *StateWithLabels) ValueSet() filters.ValueSet { return s.LabelValues } -func (ss *stateStore) PersistState(ctx context.Context, domainID string, schemaID string, data tktypes.RawJSON) (*StateWithLabels, error) { +func (ss *stateStore) PersistState(ctx context.Context, domainID, domainAddress, schemaID string, data tktypes.RawJSON) (*StateWithLabels, error) { schema, err := ss.GetSchema(ctx, domainID, schemaID, true) if err != nil { @@ -88,6 +88,7 @@ func (ss *stateStore) PersistState(ctx context.Context, domainID string, schemaI if err != nil { return nil, err } + s.DomainAddress = domainAddress op := ss.writer.newWriteOp(s.State.DomainID) op.states = []*StateWithLabels{s} @@ -194,7 +195,7 @@ func (ss *stateStore) findStates(ctx context.Context, domainID, domainAddress, s Joins("Spent", db.Select("transaction")). Joins("Locked", db.Select("transaction")). Where("domain_id = ?", domainID). - // Where("domain_address = ?", domainAddress). + Where("domain_address = ?", domainAddress). Where("schema = ?", schema.Persisted().ID) if len(excluded) > 0 { diff --git a/core/go/internal/statestore/state_status_test.go b/core/go/internal/statestore/state_status_test.go index 682982441..1eb657e3e 100644 --- a/core/go/internal/statestore/state_status_test.go +++ b/core/go/internal/statestore/state_status_test.go @@ -63,7 +63,7 @@ func makeWidgets(t *testing.T, ctx context.Context, ss *stateStore, domainID, sc ij["salt"] = tktypes.RandHex(32) withSalt, err := json.Marshal(ij) require.NoError(t, err) - states[i], err = ss.PersistState(ctx, domainID, schemaID, withSalt) + states[i], err = ss.PersistState(ctx, domainID, "0x1234", schemaID, withSalt) require.NoError(t, err) fmt.Printf("widget[%d]: %s\n", i, states[i].Data) } diff --git a/core/go/internal/statestore/state_test.go b/core/go/internal/statestore/state_test.go index 19b8d9e93..407327704 100644 --- a/core/go/internal/statestore/state_test.go +++ b/core/go/internal/statestore/state_test.go @@ -34,7 +34,7 @@ func TestPersistStateMissingSchema(t *testing.T) { db.ExpectQuery("SELECT").WillReturnRows(db.NewRows([]string{})) - _, err := ss.PersistState(ctx, "domain1", tktypes.Bytes32Keccak(([]byte)("test")).String(), nil) + _, err := ss.PersistState(ctx, "domain1", "0x1234", tktypes.Bytes32Keccak(([]byte)("test")).String(), nil) assert.Regexp(t, "PD010106", err) } @@ -48,7 +48,7 @@ func TestPersistStateInvalidState(t *testing.T) { definition: &abi.Parameter{}, }) - _, err := ss.PersistState(ctx, "domain1", schemaID.String(), nil) + _, err := ss.PersistState(ctx, "domain1", "0x1234", schemaID.String(), nil) assert.Regexp(t, "PD010116", err) } diff --git a/core/go/internal/statestore/statestore_rpc.go b/core/go/internal/statestore/statestore_rpc.go index 29adff531..ecc032d7f 100644 --- a/core/go/internal/statestore/statestore_rpc.go +++ b/core/go/internal/statestore/statestore_rpc.go @@ -44,13 +44,14 @@ func (ss *stateStore) rpcListSchema() rpcserver.RPCHandler { } func (ss *stateStore) rpcStoreState() rpcserver.RPCHandler { - return rpcserver.RPCMethod3(func(ctx context.Context, + return rpcserver.RPCMethod4(func(ctx context.Context, domain string, + domainAddress string, schema string, value tktypes.RawJSON, ) (*State, error) { var state *State - newState, err := ss.PersistState(ctx, domain, schema, value) + newState, err := ss.PersistState(ctx, domain, domainAddress, schema, value) if err == nil { state = newState.State } diff --git a/core/go/internal/statestore/statestore_rpc_test.go b/core/go/internal/statestore/statestore_rpc_test.go index ed89673ed..689539411 100644 --- a/core/go/internal/statestore/statestore_rpc_test.go +++ b/core/go/internal/statestore/statestore_rpc_test.go @@ -82,7 +82,7 @@ func TestRPC(t *testing.T) { assert.Equal(t, "0x3612029bf239cbed1e27548e9211ecfe72496dfec4183fd3ea79a3a54eb126be", schemas[0].ID.String()) var state *State - rpcErr = c.CallRPC(ctx, &state, "pstate_storeState", "domain1", schemas[0].ID, tktypes.RawJSON(`{ + rpcErr = c.CallRPC(ctx, &state, "pstate_storeState", "domain1", "0x1234", schemas[0].ID, tktypes.RawJSON(`{ "salt": "fd2724ce91a859e24c228e50ae17b9443454514edce9a64437c208b0184d8910", "size": 10, "color": "blue", diff --git a/core/go/pkg/testbed/ut_simdomain_notarized_token_test.go b/core/go/pkg/testbed/ut_simdomain_notarized_token_test.go index 33dbdb509..cc3ac59a2 100644 --- a/core/go/pkg/testbed/ut_simdomain_notarized_token_test.go +++ b/core/go/pkg/testbed/ut_simdomain_notarized_token_test.go @@ -137,7 +137,7 @@ func TestDemoNotarizedCoinSelection(t *testing.T) { var fakeCoinSchemaID string var chainID int64 - fakeCoinSelection := func(ctx context.Context, fromAddr *ethtypes.Address0xHex, amount *big.Int) ([]*fakeCoinParser, []*prototk.StateRef, *big.Int, error) { + fakeCoinSelection := func(ctx context.Context, fromAddr *ethtypes.Address0xHex, contractAddr string, amount *big.Int) ([]*fakeCoinParser, []*prototk.StateRef, *big.Int, error) { var lastStateTimestamp int64 total := big.NewInt(0) coins := []*fakeCoinParser{} @@ -161,8 +161,9 @@ func TestDemoNotarizedCoinSelection(t *testing.T) { } } res, err := callbacks.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - SchemaId: fakeCoinSchemaID, - QueryJson: tktypes.JSONString(jq).String(), + DomainAddress: contractAddr, + SchemaId: fakeCoinSchemaID, + QueryJson: tktypes.JSONString(jq).String(), }) if err != nil { return nil, nil, nil, err @@ -380,7 +381,7 @@ func TestDemoNotarizedCoinSelection(t *testing.T) { coinsToSpend := []*fakeCoinParser{} stateRefsToSpend := []*prototk.StateRef{} if txInputs.From != "" { - coinsToSpend, stateRefsToSpend, toKeep, err = fakeCoinSelection(ctx, fromAddr, amount) + coinsToSpend, stateRefsToSpend, toKeep, err = fakeCoinSelection(ctx, fromAddr, req.Transaction.ContractAddress, amount) if err != nil { return nil, err } diff --git a/domains/noto/internal/noto/e2e_noto_test.go b/domains/noto/internal/noto/e2e_noto_test.go index 8e2424d87..4d2fc716c 100644 --- a/domains/noto/internal/noto/e2e_noto_test.go +++ b/domains/noto/internal/noto/e2e_noto_test.go @@ -170,7 +170,7 @@ func TestNoto(t *testing.T) { } assert.True(t, boolResult) - coins, err := noto.FindCoins(ctx, "{}") + coins, err := noto.FindCoins(ctx, notoAddress.String(), "{}") require.NoError(t, err) require.Len(t, coins, 1) assert.Equal(t, int64(100), coins[0].Amount.Int64()) @@ -217,7 +217,7 @@ func TestNoto(t *testing.T) { require.NoError(t, rpcerr.Error()) } - coins, err = noto.FindCoins(ctx, "{}") + coins, err = noto.FindCoins(ctx, notoAddress.String(), "{}") require.NoError(t, err) require.Len(t, coins, 3) @@ -246,7 +246,7 @@ func TestNoto(t *testing.T) { require.NoError(t, rpcerr.Error()) } - coins, err = noto.FindCoins(ctx, "{}") + coins, err = noto.FindCoins(ctx, notoAddress.String(), "{}") require.NoError(t, err) require.Len(t, coins, 4) // TODO: verify coins } @@ -405,7 +405,7 @@ func TestNotoSelfSubmit(t *testing.T) { } assert.True(t, boolResult) - coins, err := noto.FindCoins(ctx, "{}") + coins, err := noto.FindCoins(ctx, notoAddress.String(), "{}") require.NoError(t, err) assert.Len(t, coins, 1) assert.Equal(t, int64(100), coins[0].Amount.Int64()) @@ -425,7 +425,7 @@ func TestNotoSelfSubmit(t *testing.T) { require.NoError(t, rpcerr.Error()) } - coins, err = noto.FindCoins(ctx, "{}") + coins, err = noto.FindCoins(ctx, notoAddress.String(), "{}") require.NoError(t, err) assert.Len(t, coins, 3) // TODO: verify coins @@ -443,7 +443,7 @@ func TestNotoSelfSubmit(t *testing.T) { require.NoError(t, rpcerr.Error()) } - coins, err = noto.FindCoins(ctx, "{}") + coins, err = noto.FindCoins(ctx, notoAddress.String(), "{}") require.NoError(t, err) assert.Len(t, coins, 4) // TODO: verify coins } diff --git a/domains/noto/internal/noto/handler_transfer.go b/domains/noto/internal/noto/handler_transfer.go index 09a90d763..c870b6e67 100644 --- a/domains/noto/internal/noto/handler_transfer.go +++ b/domains/noto/internal/noto/handler_transfer.go @@ -92,7 +92,7 @@ func (h *transferHandler) Assemble(ctx context.Context, tx *types.ParsedTransact return nil, err } - inputCoins, inputStates, total, err := h.noto.prepareInputs(ctx, *fromAddress, params.Amount) + inputCoins, inputStates, total, err := h.noto.prepareInputs(ctx, req.Transaction.ContractAddress, *fromAddress, params.Amount) if err != nil { return nil, err } diff --git a/domains/noto/internal/noto/noto.go b/domains/noto/internal/noto/noto.go index bd2d2f278..208eb42af 100644 --- a/domains/noto/internal/noto/noto.go +++ b/domains/noto/internal/noto/noto.go @@ -327,8 +327,8 @@ func (n *Noto) gatherCoins(ctx context.Context, inputs, outputs []*pb.Endorsable }, nil } -func (n *Noto) FindCoins(ctx context.Context, query string) ([]*types.NotoCoin, error) { - states, err := n.findAvailableStates(ctx, query) +func (n *Noto) FindCoins(ctx context.Context, domainAddress, query string) ([]*types.NotoCoin, error) { + states, err := n.findAvailableStates(ctx, domainAddress, query) if err != nil { return nil, err } diff --git a/domains/noto/internal/noto/states.go b/domains/noto/internal/noto/states.go index 483d0c6b7..2cb85dbf4 100644 --- a/domains/noto/internal/noto/states.go +++ b/domains/noto/internal/noto/states.go @@ -83,7 +83,7 @@ func (n *Noto) makeNewState(coin *types.NotoCoin) (*pb.NewState, error) { }, nil } -func (n *Noto) prepareInputs(ctx context.Context, owner ethtypes.Address0xHex, amount *ethtypes.HexInteger) ([]*types.NotoCoin, []*pb.StateRef, *big.Int, error) { +func (n *Noto) prepareInputs(ctx context.Context, domainAddress string, owner ethtypes.Address0xHex, amount *ethtypes.HexInteger) ([]*types.NotoCoin, []*pb.StateRef, *big.Int, error) { var lastStateTimestamp int64 total := big.NewInt(0) stateRefs := []*pb.StateRef{} @@ -99,7 +99,7 @@ func (n *Noto) prepareInputs(ctx context.Context, owner ethtypes.Address0xHex, a } log.L(ctx).Debugf("State query: %s", queryBuilder.Query()) - states, err := n.findAvailableStates(ctx, queryBuilder.Query().String()) + states, err := n.findAvailableStates(ctx, domainAddress, queryBuilder.Query().String()) if err != nil { return nil, nil, nil, err @@ -139,10 +139,11 @@ func (n *Noto) prepareOutputs(owner ethtypes.Address0xHex, amount *ethtypes.HexI return []*types.NotoCoin{newCoin}, []*pb.NewState{newState}, err } -func (n *Noto) findAvailableStates(ctx context.Context, query string) ([]*pb.StoredState, error) { +func (n *Noto) findAvailableStates(ctx context.Context, domainAddress, query string) ([]*pb.StoredState, error) { req := &pb.FindAvailableStatesRequest{ - SchemaId: n.coinSchema.Id, - QueryJson: query, + DomainAddress: domainAddress, + SchemaId: n.coinSchema.Id, + QueryJson: query, } res, err := n.Callbacks.FindAvailableStates(ctx, req) if err != nil { diff --git a/domains/noto/pkg/noto/noto.go b/domains/noto/pkg/noto/noto.go index cba377ff9..e6e0419a8 100644 --- a/domains/noto/pkg/noto/noto.go +++ b/domains/noto/pkg/noto/noto.go @@ -26,7 +26,7 @@ import ( type Noto interface { plugintk.DomainAPI GetHandler(method string) types.DomainHandler - FindCoins(ctx context.Context, query string) ([]*types.NotoCoin, error) + FindCoins(ctx context.Context, domainAddress, query string) ([]*types.NotoCoin, error) } func New(callbacks plugintk.DomainCallbacks) Noto { diff --git a/domains/pente/src/main/java/io/kaleido/paladin/pente/domain/PenteDomain.java b/domains/pente/src/main/java/io/kaleido/paladin/pente/domain/PenteDomain.java index 82b544e10..7ba76e0a8 100644 --- a/domains/pente/src/main/java/io/kaleido/paladin/pente/domain/PenteDomain.java +++ b/domains/pente/src/main/java/io/kaleido/paladin/pente/domain/PenteDomain.java @@ -179,7 +179,7 @@ protected CompletableFuture assembleTransa var tx = new PenteTransaction(this, request.getTransaction()); // Execution throws an EVMExecutionException if fails - var accountLoader = new AssemblyAccountLoader(); + var accountLoader = new AssemblyAccountLoader(request.getTransaction().getContractAddress()); var execResult = tx.executeEVM(config.getChainId(), tx.getFromVerifier(request.getResolvedVerifiersList()), accountLoader); var result = ToDomain.AssembleTransactionResponse.newBuilder(); var assembledTransaction = tx.buildAssembledTransaction(execResult.evm(), accountLoader); @@ -357,6 +357,12 @@ protected CompletableFuture prepareTransact /** during assembly we load available states from the Paladin state store */ class AssemblyAccountLoader implements AccountLoader { private final HashMap loadedAccountStates = new HashMap<>(); + private final String domainAddress; + + AssemblyAccountLoader(String domainAddress) { + this.domainAddress = domainAddress; + } + public Optional load(org.hyperledger.besu.datatypes.Address address) throws IOException { return withIOException(() -> { var queryJson = JsonQuery.newBuilder(). @@ -364,6 +370,7 @@ public Optional load(org.hyperledger.besu.datatypes.Address ad isEqual("address", address.toString()). json(); var response = findAvailableStates(FromDomain.FindAvailableStatesRequest.newBuilder(). + setDomainAddress(this.domainAddress). setSchemaId(config.schemaId_AccountStateLatest()). setQueryJson(queryJson). build()).get(); diff --git a/domains/zeto/integration-test/e2e_test.go b/domains/zeto/integration-test/e2e_test.go index d09a058d5..d4a49a7e7 100644 --- a/domains/zeto/integration-test/e2e_test.go +++ b/domains/zeto/integration-test/e2e_test.go @@ -205,7 +205,7 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string) { } assert.True(t, boolResult) - coins, err := s.domain.FindCoins(ctx, "{}") + coins, err := s.domain.FindCoins(ctx, zetoAddress.String(), "{}") require.NoError(t, err) require.Len(t, coins, 1) assert.Equal(t, int64(10), coins[0].Amount.Int64()) @@ -226,7 +226,7 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string) { } assert.True(t, boolResult) - coins, err = s.domain.FindCoins(ctx, "{}") + coins, err = s.domain.FindCoins(ctx, zetoAddress.String(), "{}") require.NoError(t, err) require.Len(t, coins, 2) assert.Equal(t, int64(10), coins[0].Amount.Int64()) diff --git a/domains/zeto/internal/zeto/handler_transfer.go b/domains/zeto/internal/zeto/handler_transfer.go index 66e338164..5e89d2770 100644 --- a/domains/zeto/internal/zeto/handler_transfer.go +++ b/domains/zeto/internal/zeto/handler_transfer.go @@ -142,7 +142,7 @@ func (h *transferHandler) Assemble(ctx context.Context, tx *types.ParsedTransact return nil, err } - inputCoins, inputStates, total, err := h.zeto.prepareInputs(ctx, tx.Transaction.From, params.Amount) + inputCoins, inputStates, total, err := h.zeto.prepareInputs(ctx, req.Transaction.ContractAddress, tx.Transaction.From, params.Amount) if err != nil { return nil, err } diff --git a/domains/zeto/internal/zeto/states.go b/domains/zeto/internal/zeto/states.go index 69d7d91e3..1015aafb1 100644 --- a/domains/zeto/internal/zeto/states.go +++ b/domains/zeto/internal/zeto/states.go @@ -50,7 +50,7 @@ func (z *Zeto) makeNewState(coin *types.ZetoCoin) (*pb.NewState, error) { }, nil } -func (z *Zeto) prepareInputs(ctx context.Context, owner string, amount *ethtypes.HexInteger) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, error) { +func (z *Zeto) prepareInputs(ctx context.Context, domainAddress, owner string, amount *ethtypes.HexInteger) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, error) { var lastStateTimestamp int64 total := big.NewInt(0) stateRefs := []*pb.StateRef{} @@ -64,7 +64,7 @@ func (z *Zeto) prepareInputs(ctx context.Context, owner string, amount *ethtypes if lastStateTimestamp > 0 { queryBuilder.GreaterThan(".created", lastStateTimestamp) } - states, err := z.findAvailableStates(ctx, queryBuilder.Query().String()) + states, err := z.findAvailableStates(ctx, domainAddress, queryBuilder.Query().String()) if err != nil { return nil, nil, nil, err } @@ -126,10 +126,11 @@ func (z *Zeto) prepareOutputs(owner string, ownerKey *babyjub.PublicKey, amount return []*types.ZetoCoin{newCoin}, []*pb.NewState{newState}, err } -func (z *Zeto) findAvailableStates(ctx context.Context, query string) ([]*pb.StoredState, error) { +func (z *Zeto) findAvailableStates(ctx context.Context, domainAddress, query string) ([]*pb.StoredState, error) { req := &pb.FindAvailableStatesRequest{ - SchemaId: z.coinSchema.Id, - QueryJson: query, + DomainAddress: domainAddress, + SchemaId: z.coinSchema.Id, + QueryJson: query, } res, err := z.Callbacks.FindAvailableStates(ctx, req) if err != nil { diff --git a/domains/zeto/internal/zeto/zeto.go b/domains/zeto/internal/zeto/zeto.go index e7ff35f0b..ceecb5646 100644 --- a/domains/zeto/internal/zeto/zeto.go +++ b/domains/zeto/internal/zeto/zeto.go @@ -241,8 +241,8 @@ func (z *Zeto) validateTransaction(ctx context.Context, tx *pb.TransactionSpecif }, handler, nil } -func (z *Zeto) FindCoins(ctx context.Context, query string) ([]*types.ZetoCoin, error) { - states, err := z.findAvailableStates(ctx, query) +func (z *Zeto) FindCoins(ctx context.Context, domainAddress, query string) ([]*types.ZetoCoin, error) { + states, err := z.findAvailableStates(ctx, domainAddress, query) if err != nil { return nil, err } diff --git a/domains/zeto/pkg/zeto/zeto.go b/domains/zeto/pkg/zeto/zeto.go index ea5da1109..3ff7396fe 100644 --- a/domains/zeto/pkg/zeto/zeto.go +++ b/domains/zeto/pkg/zeto/zeto.go @@ -26,7 +26,7 @@ import ( type Zeto interface { plugintk.DomainAPI GetHandler(method string) types.DomainHandler - FindCoins(ctx context.Context, query string) ([]*types.ZetoCoin, error) + FindCoins(ctx context.Context, domainAddress, query string) ([]*types.ZetoCoin, error) } func New(callbacks plugintk.DomainCallbacks) Zeto { From 1eeb3585b48af8b07980ee4d2f82d282a1f8da2f Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 12:05:44 -0400 Subject: [PATCH 10/26] Domain address should be part of domain context Signed-off-by: Andrew Richardson --- core/go/internal/domainmgr/domain.go | 4 +- core/go/internal/domainmgr/domain_test.go | 6 +- core/go/internal/domainmgr/manager_test.go | 8 +-- .../domainmgr/private_smart_contract.go | 14 ++-- .../domainmgr/private_smart_contract_test.go | 6 +- core/go/internal/statestore/domain_context.go | 63 +++++++++--------- .../statestore/domain_context_test.go | 64 +++++++++---------- core/go/internal/statestore/schema.go | 4 +- core/go/internal/statestore/statestore.go | 6 +- 9 files changed, 89 insertions(+), 86 deletions(-) diff --git a/core/go/internal/domainmgr/domain.go b/core/go/internal/domainmgr/domain.go index 02ffb8ef6..900b03704 100644 --- a/core/go/internal/domainmgr/domain.go +++ b/core/go/internal/domainmgr/domain.go @@ -198,8 +198,8 @@ func (d *domain) FindAvailableStates(ctx context.Context, req *prototk.FindAvail } var states []*statestore.State - err = d.dm.stateStore.RunInDomainContext(d.name, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { - states, err = dsi.FindAvailableStates(req.DomainAddress, req.SchemaId, &query) + err = d.dm.stateStore.RunInDomainContext(d.name, req.DomainAddress, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { + states, err = dsi.FindAvailableStates(req.SchemaId, &query) return err }) if err != nil { diff --git a/core/go/internal/domainmgr/domain_test.go b/core/go/internal/domainmgr/domain_test.go index 5c9ea0218..44b96b876 100644 --- a/core/go/internal/domainmgr/domain_test.go +++ b/core/go/internal/domainmgr/domain_test.go @@ -323,7 +323,7 @@ func TestDomainFindAvailableStatesBadQuery(t *testing.T) { func TestDomainFindAvailableStatesFail(t *testing.T) { ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), func(mc *mockComponents) { mc.stateStore.On("EnsureABISchemas", mock.Anything, "test1", mock.Anything).Return([]statestore.Schema{}, nil) - mc.domainStateInterface.On("FindAvailableStates", "0x1234", "12345", mock.Anything).Return(nil, fmt.Errorf("pop")) + mc.domainStateInterface.On("FindAvailableStates", "12345", mock.Anything).Return(nil, fmt.Errorf("pop")) }) defer done() assert.Nil(t, tp.d.initError.Load()) @@ -344,8 +344,8 @@ func storeState(t *testing.T, dm *domainManager, tp *testPlugin, domainAddress s stateJSON, err := json.Marshal(state) require.NoError(t, err) - err = dm.stateStore.RunInDomainContextFlush("test1", func(ctx context.Context, dsi statestore.DomainStateInterface) error { - newStates, err := dsi.UpsertStates(domainAddress, &txID, []*statestore.StateUpsert{ + err = dm.stateStore.RunInDomainContextFlush("test1", domainAddress, func(ctx context.Context, dsi statestore.DomainStateInterface) error { + newStates, err := dsi.UpsertStates(&txID, []*statestore.StateUpsert{ { SchemaID: tp.stateSchemas[0].Id, Data: stateJSON, diff --git a/core/go/internal/domainmgr/manager_test.go b/core/go/internal/domainmgr/manager_test.go index 6cf4809e6..155836b97 100644 --- a/core/go/internal/domainmgr/manager_test.go +++ b/core/go/internal/domainmgr/manager_test.go @@ -79,15 +79,15 @@ func newTestDomainManager(t *testing.T, realDB bool, conf *DomainManagerConfig, require.NoError(t, mp.Mock.ExpectationsWereMet()) } componentMocks.On("StateStore").Return(mc.stateStore) - mridc := mc.stateStore.On("RunInDomainContext", mock.Anything, mock.Anything) + mridc := mc.stateStore.On("RunInDomainContext", mock.Anything, mock.Anything, mock.Anything) mridc.Run(func(args mock.Arguments) { - mridc.Return((args[1].(statestore.DomainContextFunction))( + mridc.Return((args[2].(statestore.DomainContextFunction))( ctx, mc.domainStateInterface, )) }).Maybe() - mridcf := mc.stateStore.On("RunInDomainContextFlush", mock.Anything, mock.Anything) + mridcf := mc.stateStore.On("RunInDomainContextFlush", mock.Anything, mock.Anything, mock.Anything) mridcf.Run(func(args mock.Arguments) { - mridcf.Return((args[1].(statestore.DomainContextFunction))( + mridcf.Return((args[2].(statestore.DomainContextFunction))( ctx, mc.domainStateInterface, )) }).Maybe() diff --git a/core/go/internal/domainmgr/private_smart_contract.go b/core/go/internal/domainmgr/private_smart_contract.go index 882c76fcd..bf1cde9d6 100644 --- a/core/go/internal/domainmgr/private_smart_contract.go +++ b/core/go/internal/domainmgr/private_smart_contract.go @@ -189,8 +189,8 @@ func (dc *domainContract) WritePotentialStates(ctx context.Context, tx *componen var states []*statestore.State if len(newStatesToWrite) > 0 { - err := dc.dm.stateStore.RunInDomainContext(domain.name, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { - states, err = dsi.UpsertStates(dc.info.Address.String(), &tx.ID, newStatesToWrite) + err := dc.dm.stateStore.RunInDomainContext(domain.name, dc.info.Address.String(), func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { + states, err = dsi.UpsertStates(&tx.ID, newStatesToWrite) return err }) if err != nil { @@ -254,11 +254,11 @@ func (dc *domainContract) LockStates(ctx context.Context, tx *components.Private }) } - return dc.dm.stateStore.RunInDomainContext(dc.d.name, func(ctx context.Context, dsi statestore.DomainStateInterface) error { + return dc.dm.stateStore.RunInDomainContext(dc.d.name, dc.info.Address.String(), func(ctx context.Context, dsi statestore.DomainStateInterface) error { // Heavy lifting is all done for us by the state store - _, err := dsi.UpsertStates(dc.info.Address.String(), &tx.ID, txLockedStateUpserts) + _, err := dsi.UpsertStates(&tx.ID, txLockedStateUpserts) if err == nil && len(readStateUpserts) > 0 { - _, err = dsi.UpsertStates(dc.info.Address.String(), nil, readStateUpserts) + _, err = dsi.UpsertStates(nil, readStateUpserts) } return err }) @@ -416,9 +416,9 @@ func (dc *domainContract) loadStates(ctx context.Context, refs []*prototk.StateR stateIDs[i] = stateID } statesByID := make(map[tktypes.Bytes32]*statestore.State) - err := dc.dm.stateStore.RunInDomainContext(dc.d.name, func(ctx context.Context, dsi statestore.DomainStateInterface) error { + err := dc.dm.stateStore.RunInDomainContext(dc.d.name, dc.info.Address.String(), func(ctx context.Context, dsi statestore.DomainStateInterface) error { for schemaID, stateIDs := range rawIDsBySchema { - statesForSchema, err := dsi.FindAvailableStates(dc.info.Address.String(), schemaID, &query.QueryJSON{ + statesForSchema, err := dsi.FindAvailableStates(schemaID, &query.QueryJSON{ Statements: query.Statements{ Ops: query.Ops{ In: []*query.OpMultiVal{ diff --git a/core/go/internal/domainmgr/private_smart_contract_test.go b/core/go/internal/domainmgr/private_smart_contract_test.go index 6c752b8c0..ee896252e 100644 --- a/core/go/internal/domainmgr/private_smart_contract_test.go +++ b/core/go/internal/domainmgr/private_smart_contract_test.go @@ -585,7 +585,7 @@ func TestDomainWritePotentialStatesFail(t *testing.T) { schema.On("IDString").Return("schema1") schema.On("Signature").Return("schema1_signature") ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), mockSchemas(schema), mockBlockHeight, func(mc *mockComponents) { - mc.domainStateInterface.On("UpsertStates", mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) + mc.domainStateInterface.On("UpsertStates", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) }) defer done() @@ -733,7 +733,7 @@ func TestPrepareTransactionBadData(t *testing.T) { func TestLoadStatesError(t *testing.T) { ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), mockSchemas(), mockBlockHeight, func(mc *mockComponents) { - mc.domainStateInterface.On("FindAvailableStates", mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) + mc.domainStateInterface.On("FindAvailableStates", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) }) defer done() @@ -748,7 +748,7 @@ func TestLoadStatesError(t *testing.T) { func TestLoadStatesNotFound(t *testing.T) { ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), mockSchemas(), mockBlockHeight, func(mc *mockComponents) { - mc.domainStateInterface.On("FindAvailableStates", mock.Anything, mock.Anything, mock.Anything).Return([]*statestore.State{}, nil) + mc.domainStateInterface.On("FindAvailableStates", mock.Anything, mock.Anything).Return([]*statestore.State{}, nil) }) defer done() diff --git a/core/go/internal/statestore/domain_context.go b/core/go/internal/statestore/domain_context.go index 061f66629..de3859eb1 100644 --- a/core/go/internal/statestore/domain_context.go +++ b/core/go/internal/statestore/domain_context.go @@ -52,7 +52,7 @@ type DomainStateInterface interface { // 2) We deliberately return states that are locked to a transaction (but not spent yet) - which means the // result of the any assemble that uses those states, will be a transaction that must // be on the same transaction where those states are locked. - FindAvailableStates(domainAddress, schemaID string, query *query.QueryJSON) (s []*State, err error) + FindAvailableStates(schemaID string, query *query.QueryJSON) (s []*State, err error) // MarkStatesSpending writes a lock record so the state is now locked for spending, and // thus subsequent calls to FindAvailableStates will not return these states. @@ -70,7 +70,7 @@ type DomainStateInterface interface { // If a non-nil transaction ID is supplied, then the states are mark locked to the specified // transaction. They can then be locked-for-creation, locked-for-spending, for simply // locked for existence to avoid other transactions spending them. - UpsertStates(domainAddress string, transactionID *uuid.UUID, states []*StateUpsert) (s []*State, err error) + UpsertStates(transactionID *uuid.UUID, states []*StateUpsert) (s []*State, err error) // ResetTransaction queues up removal of all lock records for a given transaction // Note that the private data of the states themselves are not removed @@ -97,22 +97,23 @@ type DomainStateInterface interface { } type domainContext struct { - ss *stateStore - domainID string - ctx context.Context - stateLock sync.Mutex - latch chan struct{} - unFlushed *writeOperation - flushing *writeOperation - flushResult chan error + ss *stateStore + domainName string + domainAddress string + ctx context.Context + stateLock sync.Mutex + latch chan struct{} + unFlushed *writeOperation + flushing *writeOperation + flushResult chan error } -func (ss *stateStore) RunInDomainContext(domainID string, fn DomainContextFunction) error { - return ss.getDomainContext(domainID).run(fn) +func (ss *stateStore) RunInDomainContext(domainName, domainAddress string, fn DomainContextFunction) error { + return ss.getDomainContext(domainName, domainAddress).run(fn) } -func (ss *stateStore) RunInDomainContextFlush(domainID string, fn DomainContextFunction) error { - dc := ss.getDomainContext(domainID) +func (ss *stateStore) RunInDomainContextFlush(domainName, domainAddress string, fn DomainContextFunction) error { + dc := ss.getDomainContext(domainName, domainAddress) err := dc.run(fn) if err == nil { err = dc.Flush() @@ -123,20 +124,22 @@ func (ss *stateStore) RunInDomainContextFlush(domainID string, fn DomainContextF return err } -func (ss *stateStore) getDomainContext(domainID string) *domainContext { +func (ss *stateStore) getDomainContext(domainName, domainAddress string) *domainContext { ss.domainLock.Lock() defer ss.domainLock.Unlock() - dc := ss.domainContexts[domainID] + domainKey := domainName + ":" + contractAddress + dc := ss.domainContexts[domainKey] if dc == nil { dc = &domainContext{ - ss: ss, - domainID: domainID, - ctx: log.WithLogField(ss.bgCtx, "domain_context", domainID), - latch: make(chan struct{}, 1), - unFlushed: ss.writer.newWriteOp(domainID), + ss: ss, + domainName: domainName, + domainAddress: domainAddress, + ctx: log.WithLogField(ss.bgCtx, "domain_context", domainName), + latch: make(chan struct{}, 1), + unFlushed: ss.writer.newWriteOp(domainName), } - ss.domainContexts[domainID] = dc + ss.domainContexts[domainKey] = dc } return dc } @@ -291,7 +294,7 @@ func (dc *domainContext) mergeInMemoryMatches(schema Schema, states []*State, ex } -func (dc *domainContext) FindAvailableStates(domainAddress, schemaID string, query *query.QueryJSON) (s []*State, err error) { +func (dc *domainContext) FindAvailableStates(schemaID string, query *query.QueryJSON) (s []*State, err error) { // Build a list of excluded states excluded, err := dc.getUnFlushedSpending() @@ -300,7 +303,7 @@ func (dc *domainContext) FindAvailableStates(domainAddress, schemaID string, que } // Run the query against the DB - schema, states, err := dc.ss.findStates(dc.ctx, dc.domainID, domainAddress, schemaID, query, StateStatusAvailable, excluded...) + schema, states, err := dc.ss.findStates(dc.ctx, dc.domainName, dc.domainAddress, schemaID, query, StateStatusAvailable, excluded...) if err != nil { return nil, err } @@ -309,12 +312,12 @@ func (dc *domainContext) FindAvailableStates(domainAddress, schemaID string, que return dc.mergedUnFlushed(schema, states, query) } -func (dc *domainContext) UpsertStates(domainAddress string, transactionID *uuid.UUID, stateUpserts []*StateUpsert) (states []*State, err error) { +func (dc *domainContext) UpsertStates(transactionID *uuid.UUID, stateUpserts []*StateUpsert) (states []*State, err error) { states = make([]*State, len(stateUpserts)) withValues := make([]*StateWithLabels, len(stateUpserts)) for i, ns := range stateUpserts { - schema, err := dc.ss.GetSchema(dc.ctx, dc.domainID, ns.SchemaID, true) + schema, err := dc.ss.GetSchema(dc.ctx, dc.domainName, ns.SchemaID, true) if err != nil { return nil, err } @@ -324,7 +327,7 @@ func (dc *domainContext) UpsertStates(domainAddress string, transactionID *uuid. return nil, err } states[i] = withValues[i].State - states[i].DomainAddress = domainAddress + states[i].DomainAddress = dc.domainAddress if transactionID != nil { states[i].Locked = &StateLock{ Transaction: *transactionID, @@ -466,7 +469,7 @@ func (dc *domainContext) Flush(successCallbacks ...DomainContextFunction) error // Cycle it out dc.flushing = dc.unFlushed dc.flushResult = make(chan error, 1) - dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainID) + dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName) // We pass the vars directly to the routine, so the routine does not need the lock go dc.flushOp(dc.flushing, dc.flushResult, successCallbacks...) @@ -507,8 +510,8 @@ func (dc *domainContext) checkFlushCompletion(block bool) error { dc.flushResult = nil // If there was an error, we need to clean out the whole un-flushed state before we return it if flushErr != nil { - dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainID) - return i18n.WrapError(dc.ctx, flushErr, msgs.MsgStateFlushFailedDomainReset, dc.domainID) + dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName) + return i18n.WrapError(dc.ctx, flushErr, msgs.MsgStateFlushFailedDomainReset, dc.domainName) } return nil } diff --git a/core/go/internal/statestore/domain_context_test.go b/core/go/internal/statestore/domain_context_test.go index 39d89e723..6528f2433 100644 --- a/core/go/internal/statestore/domain_context_test.go +++ b/core/go/internal/statestore/domain_context_test.go @@ -72,7 +72,7 @@ func TestStateFlushAsync(t *testing.T) { defer done() flushed := make(chan bool) - err := ss.RunInDomainContext("domain1", func(ctx context.Context, dsi DomainStateInterface) error { + err := ss.RunInDomainContext("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { return dsi.Flush(func(ctx context.Context, dsi DomainStateInterface) error { flushed <- true return nil @@ -98,8 +98,8 @@ func TestUpsertSchemaAndStates(t *testing.T) { require.Len(t, schemas, 1) schemaID := schemas[0].IDString() - err = ss.RunInDomainContext("domain1", func(ctx context.Context, dsi DomainStateInterface) error { - states, err := dsi.UpsertStates("0x1234", nil, []*StateUpsert{ + err = ss.RunInDomainContext("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + states, err := dsi.UpsertStates(nil, []*StateUpsert{ { SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 100, "owner": "0x1eDfD974fE6828dE81a1a762df680111870B7cDD", "salt": "%s"}`, tktypes.RandHex(32))), @@ -126,10 +126,10 @@ func TestStateContextMintSpendMint(t *testing.T) { assert.Len(t, schemas, 1) schemaID = schemas[0].IDString() - err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { + err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { // Store some states - tx1states, err := dsi.UpsertStates("0x1234", &transactionID, []*StateUpsert{ + tx1states, err := dsi.UpsertStates(&transactionID, []*StateUpsert{ {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 100, "owner": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 10, "owner": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 75, "owner": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, @@ -149,7 +149,7 @@ func TestStateContextMintSpendMint(t *testing.T) { // Query the states, and notice we find the ones that are still in the process of creating // even though they've not yet been written to the DB - states, err := dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ + states, err := dsi.FindAvailableStates(schemaID, toQuery(t, `{ "sort": [ "amount" ] }`)) require.NoError(t, err) @@ -172,7 +172,7 @@ func TestStateContextMintSpendMint(t *testing.T) { // Do a quick check on upsert semantics with un-flushed updates, to make sure the unflushed list doesn't dup tx2Salts := []string{tktypes.RandHex(32), tktypes.RandHex(32)} for dup := 0; dup < 2; dup++ { - tx2states, err := dsi.UpsertStates("0x1234", &transactionID, []*StateUpsert{ + tx2states, err := dsi.UpsertStates(&transactionID, []*StateUpsert{ {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 35, "owner": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180", "salt": "%s"}`, tx2Salts[0])), Creating: true}, {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 50, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tx2Salts[1])), Creating: true}, }) @@ -183,7 +183,7 @@ func TestStateContextMintSpendMint(t *testing.T) { } // Query the states on the first address - states, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ + states, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{ "sort": [ "-amount" ], "eq": [{"field": "owner", "value": "0xf7b1c69F5690993F2C8ecE56cc89D42b1e737180"}] }`)) @@ -193,7 +193,7 @@ func TestStateContextMintSpendMint(t *testing.T) { assert.Equal(t, int64(35), parseFakeCoin(t, states[1]).Amount.Int64()) // Query the states on the other address - states, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ + states, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{ "sort": [ "-amount" ], "eq": [{"field": "owner", "value": "0x615dD09124271D8008225054d85Ffe720E7a447A"}] }`)) @@ -206,9 +206,9 @@ func TestStateContextMintSpendMint(t *testing.T) { }) require.NoError(t, err) - err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { + err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { // Check the DB persisted state is what we expect - states, err := dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ + states, err := dsi.FindAvailableStates(schemaID, toQuery(t, `{ "sort": [ "owner", "amount" ] }`)) require.NoError(t, err) @@ -228,7 +228,7 @@ func TestStateContextMintSpendMint(t *testing.T) { states[0].ID.String(), // 50 }) require.NoError(t, err) - tx3states, err := dsi.UpsertStates("0x1234", &transactionID, []*StateUpsert{ + tx3states, err := dsi.UpsertStates(&transactionID, []*StateUpsert{ {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, {SchemaID: schemaID, Data: tktypes.RawJSON(fmt.Sprintf(`{"amount": 30, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32))), Creating: true}, }) @@ -236,7 +236,7 @@ func TestStateContextMintSpendMint(t *testing.T) { assert.Len(t, tx3states, 2) // Now check that we merge the DB and in-memory state - states, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ + states, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{ "sort": [ "owner", "amount" ] }`)) require.NoError(t, err) @@ -247,7 +247,7 @@ func TestStateContextMintSpendMint(t *testing.T) { assert.Equal(t, int64(100), parseFakeCoin(t, states[3]).Amount.Int64()) // Check the limit works too across this - states, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{ + states, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{ "limit": 1, "sort": [ "owner", "amount" ] }`)) @@ -267,10 +267,10 @@ func TestStateContextMintSpendMint(t *testing.T) { }) require.NoError(t, err) - err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { + err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { // Confirm - states, err := dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, `{}`)) + states, err := dsi.FindAvailableStates(schemaID, toQuery(t, `{}`)) require.NoError(t, err) assert.Empty(t, states) @@ -284,7 +284,7 @@ func TestDSILatch(t *testing.T) { _, ss, done := newDBTestStateStore(t) - dsi := ss.getDomainContext("domain1") + dsi := ss.getDomainContext("domain1", "0x1234") err := dsi.takeLatch() require.NoError(t, err) @@ -318,12 +318,12 @@ func TestDSIFlushErrorCapture(t *testing.T) { schemas, err := ss.EnsureABISchemas(context.Background(), "domain1", []*abi.Parameter{testABIParam(t, fakeCoinABI)}) require.NoError(t, err) - err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { + err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { dc := dsi.(*domainContext) fakeFlushError(dc) - _, err = dsi.FindAvailableStates("", "", nil) + _, err = dsi.FindAvailableStates("", nil) assert.Regexp(t, "pop", err) fakeFlushError(dc) @@ -333,7 +333,7 @@ func TestDSIFlushErrorCapture(t *testing.T) { assert.Regexp(t, "pop", err) fakeFlushError(dc) - _, err = dsi.UpsertStates("0x1234", nil, nil) + _, err = dsi.UpsertStates(nil, nil) assert.Regexp(t, "pop", err) fakeFlushError(dc) @@ -367,7 +367,7 @@ func TestDSIMergedUnFlushedWhileFlushing(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1") + dc := ss.getDomainContext("domain1", "0x1234") s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, @@ -403,7 +403,7 @@ func TestDSIMergedUnFlushedWhileFlushingDedup(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1") + dc := ss.getDomainContext("domain1", "0x1234") s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, @@ -445,7 +445,7 @@ func TestDSIMergedUnFlushedEvalError(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1") + dc := ss.getDomainContext("domain1", "0x1234") s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, @@ -471,7 +471,7 @@ func TestDSIMergedInMemoryMatchesRecoverLabelsFail(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1") + dc := ss.getDomainContext("domain1", "0x1234") s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, @@ -498,7 +498,7 @@ func TestDSIMergedInMemoryMatchesSortFail(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1") + dc := ss.getDomainContext("domain1", "0x1234") s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, @@ -527,12 +527,12 @@ func TestDSIFindBadQueryAndInsert(t *testing.T) { schemaID := schemas[0].IDString() assert.Equal(t, "type=FakeCoin(bytes32 salt,address owner,uint256 amount),labels=[owner,amount]", schemas[0].Signature()) - err = ss.RunInDomainContextFlush("domain1", func(ctx context.Context, dsi DomainStateInterface) error { - _, err = dsi.FindAvailableStates("0x1234", schemaID, toQuery(t, + err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + _, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{"sort":["wrong"]}`)) assert.Regexp(t, "PD010700", err) - _, err = dsi.UpsertStates("0x1234", nil, []*StateUpsert{ + _, err = dsi.UpsertStates(nil, []*StateUpsert{ {SchemaID: schemaID, Data: tktypes.RawJSON(`"wrong"`)}, }) assert.Regexp(t, "FF22038", err) @@ -548,9 +548,9 @@ func TestDSIBadIDs(t *testing.T) { _, ss, _, done := newDBMockStateStore(t) defer done() - _ = ss.RunInDomainContext("domain1", func(ctx context.Context, dsi DomainStateInterface) error { + _ = ss.RunInDomainContext("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { - _, err := dsi.UpsertStates("0x1234", nil, []*StateUpsert{ + _, err := dsi.UpsertStates(nil, []*StateUpsert{ {SchemaID: "wrong"}, }) assert.Regexp(t, "PD020007", err) @@ -571,7 +571,7 @@ func TestDSIResetWithMixed(t *testing.T) { _, ss, _, done := newDBMockStateStore(t) defer done() - dc := ss.getDomainContext("domain1") + dc := ss.getDomainContext("domain1", "0x1234") state1 := tktypes.Bytes32Keccak(([]byte)("state1")) transactionID1 := uuid.New() @@ -594,7 +594,7 @@ func TestDSIResetWithMixed(t *testing.T) { func TestCheckEvalGTTimestamp(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - dc := ss.getDomainContext("domain1") + dc := ss.getDomainContext("domain1", "0x1234") filterJSON := `{"gt":[{"field":".created","value":1726545933211347000}],"limit":10,"sort":[".created"]}` diff --git a/core/go/internal/statestore/schema.go b/core/go/internal/statestore/schema.go index 0aa0b3039..cbe5fc452 100644 --- a/core/go/internal/statestore/schema.go +++ b/core/go/internal/statestore/schema.go @@ -163,7 +163,7 @@ func (ss *stateStore) ListSchemas(ctx context.Context, domainID string) (results return results, nil } -func (ss *stateStore) EnsureABISchemas(ctx context.Context, domainID string, defs []*abi.Parameter) ([]Schema, error) { +func (ss *stateStore) EnsureABISchemas(ctx context.Context, domainName string, defs []*abi.Parameter) ([]Schema, error) { if len(defs) == 0 { return nil, nil } @@ -172,7 +172,7 @@ func (ss *stateStore) EnsureABISchemas(ctx context.Context, domainID string, def prepared := make([]Schema, len(defs)) toFlush := make([]*SchemaPersisted, len(defs)) for i, def := range defs { - s, err := newABISchema(ctx, domainID, def) + s, err := newABISchema(ctx, domainName, def) if err != nil { return nil, err } diff --git a/core/go/internal/statestore/statestore.go b/core/go/internal/statestore/statestore.go index 29c60da3a..712f70722 100644 --- a/core/go/internal/statestore/statestore.go +++ b/core/go/internal/statestore/statestore.go @@ -46,9 +46,9 @@ var StateWriterConfigDefaults = StateWriterConfig{ type StateStore interface { RPCModule() *rpcserver.RPCModule - RunInDomainContext(domainID string, fn DomainContextFunction) error - RunInDomainContextFlush(domainID string, fn DomainContextFunction) error - EnsureABISchemas(ctx context.Context, domainID string, defs []*abi.Parameter) ([]Schema, error) + RunInDomainContext(domainName, domainAddress string, fn DomainContextFunction) error + RunInDomainContextFlush(domainName, domainAddress string, fn DomainContextFunction) error + EnsureABISchemas(ctx context.Context, domainName string, defs []*abi.Parameter) ([]Schema, error) Close() } From c0ee07feaf76717b9fbd89208a271368059505af Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 13:02:16 -0400 Subject: [PATCH 11/26] Split state writers by domain name and address Signed-off-by: Andrew Richardson --- core/go/internal/statestore/domain_context.go | 6 +++--- .../internal/statestore/domain_context_test.go | 12 ++++++++++++ core/go/internal/statestore/state.go | 18 +++++++++--------- .../internal/statestore/state_status_test.go | 10 +++++----- core/go/internal/statestore/state_test.go | 6 +++--- core/go/internal/statestore/state_writer.go | 14 +++++++------- .../internal/statestore/state_writer_test.go | 18 +++++++++--------- 7 files changed, 48 insertions(+), 36 deletions(-) diff --git a/core/go/internal/statestore/domain_context.go b/core/go/internal/statestore/domain_context.go index de3859eb1..28ab6051f 100644 --- a/core/go/internal/statestore/domain_context.go +++ b/core/go/internal/statestore/domain_context.go @@ -137,7 +137,7 @@ func (ss *stateStore) getDomainContext(domainName, domainAddress string) *domain domainAddress: domainAddress, ctx: log.WithLogField(ss.bgCtx, "domain_context", domainName), latch: make(chan struct{}, 1), - unFlushed: ss.writer.newWriteOp(domainName), + unFlushed: ss.writer.newWriteOp(domainName, domainAddress), } ss.domainContexts[domainKey] = dc } @@ -469,7 +469,7 @@ func (dc *domainContext) Flush(successCallbacks ...DomainContextFunction) error // Cycle it out dc.flushing = dc.unFlushed dc.flushResult = make(chan error, 1) - dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName) + dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName, dc.domainAddress) // We pass the vars directly to the routine, so the routine does not need the lock go dc.flushOp(dc.flushing, dc.flushResult, successCallbacks...) @@ -510,7 +510,7 @@ func (dc *domainContext) checkFlushCompletion(block bool) error { dc.flushResult = nil // If there was an error, we need to clean out the whole un-flushed state before we return it if flushErr != nil { - dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName) + dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName, dc.domainAddress) return i18n.WrapError(dc.ctx, flushErr, msgs.MsgStateFlushFailedDomainReset, dc.domainName) } return nil diff --git a/core/go/internal/statestore/domain_context_test.go b/core/go/internal/statestore/domain_context_test.go index 6528f2433..3f481934f 100644 --- a/core/go/internal/statestore/domain_context_test.go +++ b/core/go/internal/statestore/domain_context_test.go @@ -88,6 +88,17 @@ func TestStateFlushAsync(t *testing.T) { } +func TestUpsertSchemaEmptyList(t *testing.T) { + + _, ss, done := newDBTestStateStore(t) + defer done() + + schemas, err := ss.EnsureABISchemas(context.Background(), "domain1", []*abi.Parameter{}) + require.NoError(t, err) + require.Len(t, schemas, 0) + +} + func TestUpsertSchemaAndStates(t *testing.T) { _, ss, done := newDBTestStateStore(t) @@ -112,6 +123,7 @@ func TestUpsertSchemaAndStates(t *testing.T) { require.NoError(t, err) } + func TestStateContextMintSpendMint(t *testing.T) { _, ss, done := newDBTestStateStore(t) diff --git a/core/go/internal/statestore/state.go b/core/go/internal/statestore/state.go index 43a7676f2..719f0eaf7 100644 --- a/core/go/internal/statestore/state.go +++ b/core/go/internal/statestore/state.go @@ -90,7 +90,7 @@ func (ss *stateStore) PersistState(ctx context.Context, domainID, domainAddress, } s.DomainAddress = domainAddress - op := ss.writer.newWriteOp(s.State.DomainID) + op := ss.writer.newWriteOp(s.State.DomainID, domainAddress) op.states = []*StateWithLabels{s} ss.writer.queue(ctx, op) return s, op.flush(ctx) @@ -213,13 +213,13 @@ func (ss *stateStore) findStates(ctx context.Context, domainID, domainAddress, s return schema, states, nil } -func (ss *stateStore) MarkConfirmed(ctx context.Context, domainID, stateID string, transactionID uuid.UUID) error { +func (ss *stateStore) MarkConfirmed(ctx context.Context, domainID, domainAddress, stateID string, transactionID uuid.UUID) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID) + op := ss.writer.newWriteOp(domainID, domainAddress) op.stateConfirms = []*StateConfirm{ {State: id, Transaction: transactionID}, } @@ -228,13 +228,13 @@ func (ss *stateStore) MarkConfirmed(ctx context.Context, domainID, stateID strin return op.flush(ctx) } -func (ss *stateStore) MarkSpent(ctx context.Context, domainID, stateID string, transactionID uuid.UUID) error { +func (ss *stateStore) MarkSpent(ctx context.Context, domainID, domainAddress, stateID string, transactionID uuid.UUID) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID) + op := ss.writer.newWriteOp(domainID, domainAddress) op.stateSpends = []*StateSpend{ {State: id, Transaction: transactionID}, } @@ -243,13 +243,13 @@ func (ss *stateStore) MarkSpent(ctx context.Context, domainID, stateID string, t return op.flush(ctx) } -func (ss *stateStore) MarkLocked(ctx context.Context, domainID, stateID string, transactionID uuid.UUID, creating, spending bool) error { +func (ss *stateStore) MarkLocked(ctx context.Context, domainID, domainAddress, stateID string, transactionID uuid.UUID, creating, spending bool) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID) + op := ss.writer.newWriteOp(domainID, domainAddress) op.stateLocks = []*StateLock{ {State: id, Transaction: transactionID, Creating: creating, Spending: spending}, } @@ -258,8 +258,8 @@ func (ss *stateStore) MarkLocked(ctx context.Context, domainID, stateID string, return op.flush(ctx) } -func (ss *stateStore) ResetTransaction(ctx context.Context, domainID string, transactionID uuid.UUID) error { - op := ss.writer.newWriteOp(domainID) +func (ss *stateStore) ResetTransaction(ctx context.Context, domainAddress, domainID string, transactionID uuid.UUID) error { + op := ss.writer.newWriteOp(domainID, domainAddress) op.transactionLockDeletes = []uuid.UUID{transactionID} ss.writer.queue(ctx, op) diff --git a/core/go/internal/statestore/state_status_test.go b/core/go/internal/statestore/state_status_test.go index 1eb657e3e..b1b21d1a8 100644 --- a/core/go/internal/statestore/state_status_test.go +++ b/core/go/internal/statestore/state_status_test.go @@ -127,7 +127,7 @@ func TestStateLockingQuery(t *testing.T) { // Mark them all confirmed apart from one for i, w := range widgets { if i != 3 { - err = ss.MarkConfirmed(ctx, "domain1", w.ID.String(), uuid.New()) + err = ss.MarkConfirmed(ctx, "domain1", "0x1234", w.ID.String(), uuid.New()) require.NoError(t, err) } } @@ -141,7 +141,7 @@ func TestStateLockingQuery(t *testing.T) { checkQuery(`{}`, seqQual) // unchanged // Mark one spent - err = ss.MarkSpent(ctx, "domain1", widgets[0].ID.String(), uuid.New()) + err = ss.MarkSpent(ctx, "domain1", "0x1234", widgets[0].ID.String(), uuid.New()) require.NoError(t, err) checkQuery(`{}`, StateStatusAll, 0, 1, 2, 3, 4) // unchanged @@ -153,7 +153,7 @@ func TestStateLockingQuery(t *testing.T) { checkQuery(`{}`, seqQual) // unchanged // lock a confirmed one for spending - err = ss.MarkLocked(ctx, "domain1", widgets[1].ID.String(), seqID, false, true) + err = ss.MarkLocked(ctx, "domain1", "0x1234", widgets[1].ID.String(), seqID, false, true) require.NoError(t, err) checkQuery(`{}`, StateStatusAll, 0, 1, 2, 3, 4) // unchanged @@ -165,7 +165,7 @@ func TestStateLockingQuery(t *testing.T) { checkQuery(`{}`, seqQual, 1) // added 1 // lock the unconfirmed one for spending - err = ss.MarkLocked(ctx, "domain1", widgets[3].ID.String(), seqID, false, true) + err = ss.MarkLocked(ctx, "domain1", "0x1234", widgets[3].ID.String(), seqID, false, true) require.NoError(t, err) checkQuery(`{}`, StateStatusAll, 0, 1, 2, 3, 4) // unchanged @@ -181,7 +181,7 @@ func TestStateLockingQuery(t *testing.T) { checkQuery(`{"eq":[{"field":"color","value":"pink"}]}`, StateStatusAvailable) // clear the transaction locks - err = ss.ResetTransaction(ctx, "domain1", seqID) + err = ss.ResetTransaction(ctx, "domain1", "0x1234", seqID) require.NoError(t, err) checkQuery(`{}`, StateStatusAll, 0, 1, 2, 3, 4) // unchanged diff --git a/core/go/internal/statestore/state_test.go b/core/go/internal/statestore/state_test.go index 407327704..64e7dc1f6 100644 --- a/core/go/internal/statestore/state_test.go +++ b/core/go/internal/statestore/state_test.go @@ -74,7 +74,7 @@ func TestMarkConfirmedBadID(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - err := ss.MarkConfirmed(ctx, "domain1", "bad id", uuid.New()) + err := ss.MarkConfirmed(ctx, "domain1", "0x1234", "bad id", uuid.New()) assert.Regexp(t, "PD020007", err) } @@ -82,7 +82,7 @@ func TestMarkSpentBadID(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - err := ss.MarkSpent(ctx, "domain1", "bad id", uuid.New()) + err := ss.MarkSpent(ctx, "domain1", "0x1234", "bad id", uuid.New()) assert.Regexp(t, "PD020007", err) } @@ -90,7 +90,7 @@ func TestMarkLockedBadID(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - err := ss.MarkLocked(ctx, "domain1", "bad id", uuid.New(), false, false) + err := ss.MarkLocked(ctx, "domain1", "0x1234", "bad id", uuid.New(), false, false) assert.Regexp(t, "PD020007", err) } diff --git a/core/go/internal/statestore/state_writer.go b/core/go/internal/statestore/state_writer.go index 120d45326..b44fc182a 100644 --- a/core/go/internal/statestore/state_writer.go +++ b/core/go/internal/statestore/state_writer.go @@ -36,7 +36,7 @@ import ( type writeOperation struct { id string - domain string + domainKey string done chan error isShutdown bool states []*StateWithLabels @@ -86,11 +86,11 @@ func newStateWriter(bgCtx context.Context, ss *stateStore, conf *StateWriterConf return sw } -func (sw *stateWriter) newWriteOp(domain string) *writeOperation { +func (sw *stateWriter) newWriteOp(domainName, domainAddress string) *writeOperation { return &writeOperation{ - id: tktypes.ShortID(), - domain: domain, - done: make(chan error, 1), // 1 slot to ensure we don't block the writer + id: tktypes.ShortID(), + domainKey: domainName + ":" + domainAddress, + done: make(chan error, 1), // 1 slot to ensure we don't block the writer } } @@ -108,12 +108,12 @@ func (sw *stateWriter) queue(ctx context.Context, op *writeOperation) { // All insert/nonce-allocation requests for the same domain go to the same worker // currently. This allows assertions to be made between threads writing schemas, // threads writing state updates, and threads writing new states. - if op.domain == "" { + if op.domainKey == "" { op.done <- i18n.NewError(ctx, msgs.MsgStateOpInvalid) return } h := fnv.New32a() // simple non-cryptographic hash algo - _, _ = h.Write([]byte(op.domain)) + _, _ = h.Write([]byte(op.domainKey)) routine := h.Sum32() % sw.workerCount log.L(ctx).Debugf("Queuing write operation %s to worker state_writer_%.4d", op.id, routine) select { diff --git a/core/go/internal/statestore/state_writer_test.go b/core/go/internal/statestore/state_writer_test.go index 59133497e..289c23d8f 100644 --- a/core/go/internal/statestore/state_writer_test.go +++ b/core/go/internal/statestore/state_writer_test.go @@ -69,9 +69,9 @@ func TestFlushTimeout(t *testing.T) { defer done() txOp := &writeOperation{ - id: tktypes.ShortID(), - done: make(chan error, 1), - domain: "domain1", + id: tktypes.ShortID(), + done: make(chan error, 1), + domainKey: "domain1", } ss.writer.queue(ctx, txOp) closedCtx, closeCtx := context.WithCancel(ctx) @@ -85,9 +85,9 @@ func TestFlushClosed(t *testing.T) { defer done() txOp := &writeOperation{ - id: tktypes.ShortID(), - done: make(chan error, 1), - domain: "domain1", + id: tktypes.ShortID(), + done: make(chan error, 1), + domainKey: "domain1", } ss.writer.cancelCtx() <-ss.writer.workersDone[0] @@ -100,9 +100,9 @@ func TestFlushCallerClosed(t *testing.T) { defer done() txOp := &writeOperation{ - id: tktypes.ShortID(), - done: make(chan error, 1), - domain: "domain1", + id: tktypes.ShortID(), + done: make(chan error, 1), + domainKey: "domain1", } ss.writer.cancelCtx() <-ss.writer.workersDone[0] From f002c1c5d6e942e21db696aa9374c1ac4fbea316 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 13:08:27 -0400 Subject: [PATCH 12/26] Rename State.DomainAddress -> State.ContractAddress Signed-off-by: Andrew Richardson --- .../000003_create_states_table.up.sql | 12 ++--- .../sqlite/000003_create_states_table.up.sql | 12 ++--- core/go/internal/domainmgr/domain.go | 2 +- core/go/internal/domainmgr/domain_test.go | 18 +++---- .../domainmgr/private_smart_contract_test.go | 14 ++--- core/go/internal/statestore/domain_context.go | 48 ++++++++--------- core/go/internal/statestore/state.go | 52 +++++++++---------- core/go/internal/statestore/state_writer.go | 4 +- core/go/internal/statestore/statestore.go | 4 +- core/go/internal/statestore/statestore_rpc.go | 8 +-- .../ut_simdomain_notarized_token_test.go | 6 +-- domains/noto/internal/noto/noto.go | 4 +- domains/noto/internal/noto/states.go | 12 ++--- domains/noto/pkg/noto/noto.go | 2 +- .../paladin/pente/domain/PenteDomain.java | 8 +-- domains/zeto/internal/zeto/states.go | 12 ++--- domains/zeto/internal/zeto/zeto.go | 4 +- domains/zeto/pkg/zeto/zeto.go | 2 +- toolkit/proto/protos/from_domain.proto | 2 +- 19 files changed, 113 insertions(+), 113 deletions(-) diff --git a/core/go/db/migrations/postgres/000003_create_states_table.up.sql b/core/go/db/migrations/postgres/000003_create_states_table.up.sql index ed43a1373..92e4a2239 100644 --- a/core/go/db/migrations/postgres/000003_create_states_table.up.sql +++ b/core/go/db/migrations/postgres/000003_create_states_table.up.sql @@ -1,12 +1,12 @@ BEGIN; CREATE TABLE states ( - "id" TEXT NOT NULL, - "created_at" BIGINT NOT NULL, - "domain_id" TEXT, - "schema" TEXT, - "domain_address" TEXT, - "data" TEXT, + "id" TEXT NOT NULL, + "created_at" BIGINT NOT NULL, + "domain_id" TEXT, + "schema" TEXT, + "contract_address" TEXT, + "data" TEXT, PRIMARY KEY ("id"), FOREIGN KEY ("domain_id", "schema") REFERENCES schemas ("domain_id", "id") ON DELETE CASCADE ); diff --git a/core/go/db/migrations/sqlite/000003_create_states_table.up.sql b/core/go/db/migrations/sqlite/000003_create_states_table.up.sql index 80068749a..546eff023 100644 --- a/core/go/db/migrations/sqlite/000003_create_states_table.up.sql +++ b/core/go/db/migrations/sqlite/000003_create_states_table.up.sql @@ -1,10 +1,10 @@ CREATE TABLE states ( - "id" VARCHAR NOT NULL, - "created_at" BIGINT NOT NULL, - "domain_id" VARCHAR, - "schema" VARCHAR, - "domain_address" VARCHAR, - "data" VARCHAR, + "id" VARCHAR NOT NULL, + "created_at" BIGINT NOT NULL, + "domain_id" VARCHAR, + "schema" VARCHAR, + "contract_address" VARCHAR, + "data" VARCHAR, PRIMARY KEY ("id"), FOREIGN KEY ("domain_id", "schema") REFERENCES schemas ("domain_id", "id") ON DELETE CASCADE ); diff --git a/core/go/internal/domainmgr/domain.go b/core/go/internal/domainmgr/domain.go index 900b03704..3c23de4d7 100644 --- a/core/go/internal/domainmgr/domain.go +++ b/core/go/internal/domainmgr/domain.go @@ -198,7 +198,7 @@ func (d *domain) FindAvailableStates(ctx context.Context, req *prototk.FindAvail } var states []*statestore.State - err = d.dm.stateStore.RunInDomainContext(d.name, req.DomainAddress, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { + err = d.dm.stateStore.RunInDomainContext(d.name, req.ContractAddress, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { states, err = dsi.FindAvailableStates(req.SchemaId, &query) return err }) diff --git a/core/go/internal/domainmgr/domain_test.go b/core/go/internal/domainmgr/domain_test.go index 44b96b876..dc2331ab5 100644 --- a/core/go/internal/domainmgr/domain_test.go +++ b/core/go/internal/domainmgr/domain_test.go @@ -328,14 +328,14 @@ func TestDomainFindAvailableStatesFail(t *testing.T) { defer done() assert.Nil(t, tp.d.initError.Load()) _, err := tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: "0x1234", - SchemaId: "12345", - QueryJson: `{}`, + ContractAddress: "0x1234", + SchemaId: "12345", + QueryJson: `{}`, }) assert.Regexp(t, "pop", err) } -func storeState(t *testing.T, dm *domainManager, tp *testPlugin, domainAddress string, txID uuid.UUID, amount *ethtypes.HexInteger) *fakeState { +func storeState(t *testing.T, dm *domainManager, tp *testPlugin, contractAddress string, txID uuid.UUID, amount *ethtypes.HexInteger) *fakeState { state := &fakeState{ Salt: tktypes.Bytes32(tktypes.RandBytes(32)), Owner: tktypes.EthAddress(tktypes.RandBytes(20)), @@ -344,7 +344,7 @@ func storeState(t *testing.T, dm *domainManager, tp *testPlugin, domainAddress s stateJSON, err := json.Marshal(state) require.NoError(t, err) - err = dm.stateStore.RunInDomainContextFlush("test1", domainAddress, func(ctx context.Context, dsi statestore.DomainStateInterface) error { + err = dm.stateStore.RunInDomainContextFlush("test1", contractAddress, func(ctx context.Context, dsi statestore.DomainStateInterface) error { newStates, err := dsi.UpsertStates(&txID, []*statestore.StateUpsert{ { SchemaID: tp.stateSchemas[0].Id, @@ -369,8 +369,8 @@ func TestDomainFindAvailableStatesOK(t *testing.T) { // Filter match states, err := tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: "0x1234", - SchemaId: tp.stateSchemas[0].Id, + ContractAddress: "0x1234", + SchemaId: tp.stateSchemas[0].Id, QueryJson: `{ "eq": [ { "field": "owner", "value": "` + state1.Owner.String() + `" } @@ -382,8 +382,8 @@ func TestDomainFindAvailableStatesOK(t *testing.T) { // Filter miss states, err = tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: "0x1234", - SchemaId: tp.stateSchemas[0].Id, + ContractAddress: "0x1234", + SchemaId: tp.stateSchemas[0].Id, QueryJson: `{ "eq": [ { "field": "owner", "value": "` + tktypes.EthAddress(tktypes.RandBytes(20)).String() + `" } diff --git a/core/go/internal/domainmgr/private_smart_contract_test.go b/core/go/internal/domainmgr/private_smart_contract_test.go index ee896252e..39780fb03 100644 --- a/core/go/internal/domainmgr/private_smart_contract_test.go +++ b/core/go/internal/domainmgr/private_smart_contract_test.go @@ -324,8 +324,8 @@ func TestFullTransactionRealDBOK(t *testing.T) { assert.Same(t, req.Transaction, tx.PreAssembly.TransactionSpecification) stateRes, err := domain.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: req.Transaction.ContractAddress, - SchemaId: tp.stateSchemas[0].Id, + ContractAddress: req.Transaction.ContractAddress, + SchemaId: tp.stateSchemas[0].Id, QueryJson: `{ "or": [ { @@ -383,8 +383,8 @@ func TestFullTransactionRealDBOK(t *testing.T) { require.NoError(t, err) stateRes, err := domain.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: psc.Address().String(), - SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, + ContractAddress: psc.Address().String(), + SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, QueryJson: `{ "or": [ { @@ -401,9 +401,9 @@ func TestFullTransactionRealDBOK(t *testing.T) { require.NoError(t, err) stillAvailable, err := domain.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: psc.Address().String(), - SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, - QueryJson: `{}`, + ContractAddress: psc.Address().String(), + SchemaId: tx.PostAssembly.OutputStatesPotential[0].SchemaId, + QueryJson: `{}`, }) require.NoError(t, err) assert.Len(t, stillAvailable.States, 3) diff --git a/core/go/internal/statestore/domain_context.go b/core/go/internal/statestore/domain_context.go index 28ab6051f..0c6619638 100644 --- a/core/go/internal/statestore/domain_context.go +++ b/core/go/internal/statestore/domain_context.go @@ -97,23 +97,23 @@ type DomainStateInterface interface { } type domainContext struct { - ss *stateStore - domainName string - domainAddress string - ctx context.Context - stateLock sync.Mutex - latch chan struct{} - unFlushed *writeOperation - flushing *writeOperation - flushResult chan error + ss *stateStore + domainName string + contractAddress string + ctx context.Context + stateLock sync.Mutex + latch chan struct{} + unFlushed *writeOperation + flushing *writeOperation + flushResult chan error } -func (ss *stateStore) RunInDomainContext(domainName, domainAddress string, fn DomainContextFunction) error { - return ss.getDomainContext(domainName, domainAddress).run(fn) +func (ss *stateStore) RunInDomainContext(domainName, contractAddress string, fn DomainContextFunction) error { + return ss.getDomainContext(domainName, contractAddress).run(fn) } -func (ss *stateStore) RunInDomainContextFlush(domainName, domainAddress string, fn DomainContextFunction) error { - dc := ss.getDomainContext(domainName, domainAddress) +func (ss *stateStore) RunInDomainContextFlush(domainName, contractAddress string, fn DomainContextFunction) error { + dc := ss.getDomainContext(domainName, contractAddress) err := dc.run(fn) if err == nil { err = dc.Flush() @@ -124,7 +124,7 @@ func (ss *stateStore) RunInDomainContextFlush(domainName, domainAddress string, return err } -func (ss *stateStore) getDomainContext(domainName, domainAddress string) *domainContext { +func (ss *stateStore) getDomainContext(domainName, contractAddress string) *domainContext { ss.domainLock.Lock() defer ss.domainLock.Unlock() @@ -132,12 +132,12 @@ func (ss *stateStore) getDomainContext(domainName, domainAddress string) *domain dc := ss.domainContexts[domainKey] if dc == nil { dc = &domainContext{ - ss: ss, - domainName: domainName, - domainAddress: domainAddress, - ctx: log.WithLogField(ss.bgCtx, "domain_context", domainName), - latch: make(chan struct{}, 1), - unFlushed: ss.writer.newWriteOp(domainName, domainAddress), + ss: ss, + domainName: domainName, + contractAddress: contractAddress, + ctx: log.WithLogField(ss.bgCtx, "domain_context", domainName), + latch: make(chan struct{}, 1), + unFlushed: ss.writer.newWriteOp(domainName, contractAddress), } ss.domainContexts[domainKey] = dc } @@ -303,7 +303,7 @@ func (dc *domainContext) FindAvailableStates(schemaID string, query *query.Query } // Run the query against the DB - schema, states, err := dc.ss.findStates(dc.ctx, dc.domainName, dc.domainAddress, schemaID, query, StateStatusAvailable, excluded...) + schema, states, err := dc.ss.findStates(dc.ctx, dc.domainName, dc.contractAddress, schemaID, query, StateStatusAvailable, excluded...) if err != nil { return nil, err } @@ -327,7 +327,7 @@ func (dc *domainContext) UpsertStates(transactionID *uuid.UUID, stateUpserts []* return nil, err } states[i] = withValues[i].State - states[i].DomainAddress = dc.domainAddress + states[i].ContractAddress = dc.contractAddress if transactionID != nil { states[i].Locked = &StateLock{ Transaction: *transactionID, @@ -469,7 +469,7 @@ func (dc *domainContext) Flush(successCallbacks ...DomainContextFunction) error // Cycle it out dc.flushing = dc.unFlushed dc.flushResult = make(chan error, 1) - dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName, dc.domainAddress) + dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName, dc.contractAddress) // We pass the vars directly to the routine, so the routine does not need the lock go dc.flushOp(dc.flushing, dc.flushResult, successCallbacks...) @@ -510,7 +510,7 @@ func (dc *domainContext) checkFlushCompletion(block bool) error { dc.flushResult = nil // If there was an error, we need to clean out the whole un-flushed state before we return it if flushErr != nil { - dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName, dc.domainAddress) + dc.unFlushed = dc.ss.writer.newWriteOp(dc.domainName, dc.contractAddress) return i18n.WrapError(dc.ctx, flushErr, msgs.MsgStateFlushFailedDomainReset, dc.domainName) } return nil diff --git a/core/go/internal/statestore/state.go b/core/go/internal/statestore/state.go index 719f0eaf7..b97fa59eb 100644 --- a/core/go/internal/statestore/state.go +++ b/core/go/internal/statestore/state.go @@ -30,17 +30,17 @@ import ( ) type State struct { - ID tktypes.Bytes32 `json:"id" gorm:"primaryKey"` - CreatedAt tktypes.Timestamp `json:"created" gorm:"autoCreateTime:nano"` - DomainID string `json:"domain"` - DomainAddress string `json:"domainAddress"` - Schema tktypes.Bytes32 `json:"schema"` - Data tktypes.RawJSON `json:"data"` - Labels []*StateLabel `json:"-" gorm:"foreignKey:state;references:id;"` - Int64Labels []*StateInt64Label `json:"-" gorm:"foreignKey:state;references:id;"` - Confirmed *StateConfirm `json:"confirmed,omitempty" gorm:"foreignKey:state;references:id;"` - Spent *StateSpend `json:"spent,omitempty" gorm:"foreignKey:state;references:id;"` - Locked *StateLock `json:"locked,omitempty" gorm:"foreignKey:state;references:id;"` + ID tktypes.Bytes32 `json:"id" gorm:"primaryKey"` + CreatedAt tktypes.Timestamp `json:"created" gorm:"autoCreateTime:nano"` + DomainID string `json:"domain"` + Schema tktypes.Bytes32 `json:"schema"` + ContractAddress string `json:"contractAddress"` + Data tktypes.RawJSON `json:"data"` + Labels []*StateLabel `json:"-" gorm:"foreignKey:state;references:id;"` + Int64Labels []*StateInt64Label `json:"-" gorm:"foreignKey:state;references:id;"` + Confirmed *StateConfirm `json:"confirmed,omitempty" gorm:"foreignKey:state;references:id;"` + Spent *StateSpend `json:"spent,omitempty" gorm:"foreignKey:state;references:id;"` + Locked *StateLock `json:"locked,omitempty" gorm:"foreignKey:state;references:id;"` } type StateUpsert struct { @@ -77,7 +77,7 @@ func (s *StateWithLabels) ValueSet() filters.ValueSet { return s.LabelValues } -func (ss *stateStore) PersistState(ctx context.Context, domainID, domainAddress, schemaID string, data tktypes.RawJSON) (*StateWithLabels, error) { +func (ss *stateStore) PersistState(ctx context.Context, domainID, contractAddress, schemaID string, data tktypes.RawJSON) (*StateWithLabels, error) { schema, err := ss.GetSchema(ctx, domainID, schemaID, true) if err != nil { @@ -88,9 +88,9 @@ func (ss *stateStore) PersistState(ctx context.Context, domainID, domainAddress, if err != nil { return nil, err } - s.DomainAddress = domainAddress + s.ContractAddress = contractAddress - op := ss.writer.newWriteOp(s.State.DomainID, domainAddress) + op := ss.writer.newWriteOp(s.State.DomainID, contractAddress) op.states = []*StateWithLabels{s} ss.writer.queue(ctx, op) return s, op.flush(ctx) @@ -158,12 +158,12 @@ func (ss *stateStore) labelSetFor(schema Schema) *trackingLabelSet { return &tls } -func (ss *stateStore) FindStates(ctx context.Context, domainID, domainAddress, schemaID string, query *query.QueryJSON, status StateStatusQualifier) (s []*State, err error) { - _, s, err = ss.findStates(ctx, domainID, domainAddress, schemaID, query, status) +func (ss *stateStore) FindStates(ctx context.Context, domainID, contractAddress, schemaID string, query *query.QueryJSON, status StateStatusQualifier) (s []*State, err error) { + _, s, err = ss.findStates(ctx, domainID, contractAddress, schemaID, query, status) return s, err } -func (ss *stateStore) findStates(ctx context.Context, domainID, domainAddress, schemaID string, jq *query.QueryJSON, status StateStatusQualifier, excluded ...*idOnly) (schema Schema, s []*State, err error) { +func (ss *stateStore) findStates(ctx context.Context, domainID, contractAddress, schemaID string, jq *query.QueryJSON, status StateStatusQualifier, excluded ...*idOnly) (schema Schema, s []*State, err error) { if len(jq.Sort) == 0 { jq.Sort = []string{".created"} } @@ -195,7 +195,7 @@ func (ss *stateStore) findStates(ctx context.Context, domainID, domainAddress, s Joins("Spent", db.Select("transaction")). Joins("Locked", db.Select("transaction")). Where("domain_id = ?", domainID). - Where("domain_address = ?", domainAddress). + Where("contract_address = ?", contractAddress). Where("schema = ?", schema.Persisted().ID) if len(excluded) > 0 { @@ -213,13 +213,13 @@ func (ss *stateStore) findStates(ctx context.Context, domainID, domainAddress, s return schema, states, nil } -func (ss *stateStore) MarkConfirmed(ctx context.Context, domainID, domainAddress, stateID string, transactionID uuid.UUID) error { +func (ss *stateStore) MarkConfirmed(ctx context.Context, domainID, contractAddress, stateID string, transactionID uuid.UUID) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID, domainAddress) + op := ss.writer.newWriteOp(domainID, contractAddress) op.stateConfirms = []*StateConfirm{ {State: id, Transaction: transactionID}, } @@ -228,13 +228,13 @@ func (ss *stateStore) MarkConfirmed(ctx context.Context, domainID, domainAddress return op.flush(ctx) } -func (ss *stateStore) MarkSpent(ctx context.Context, domainID, domainAddress, stateID string, transactionID uuid.UUID) error { +func (ss *stateStore) MarkSpent(ctx context.Context, domainID, contractAddress, stateID string, transactionID uuid.UUID) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID, domainAddress) + op := ss.writer.newWriteOp(domainID, contractAddress) op.stateSpends = []*StateSpend{ {State: id, Transaction: transactionID}, } @@ -243,13 +243,13 @@ func (ss *stateStore) MarkSpent(ctx context.Context, domainID, domainAddress, st return op.flush(ctx) } -func (ss *stateStore) MarkLocked(ctx context.Context, domainID, domainAddress, stateID string, transactionID uuid.UUID, creating, spending bool) error { +func (ss *stateStore) MarkLocked(ctx context.Context, domainID, contractAddress, stateID string, transactionID uuid.UUID, creating, spending bool) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID, domainAddress) + op := ss.writer.newWriteOp(domainID, contractAddress) op.stateLocks = []*StateLock{ {State: id, Transaction: transactionID, Creating: creating, Spending: spending}, } @@ -258,8 +258,8 @@ func (ss *stateStore) MarkLocked(ctx context.Context, domainID, domainAddress, s return op.flush(ctx) } -func (ss *stateStore) ResetTransaction(ctx context.Context, domainAddress, domainID string, transactionID uuid.UUID) error { - op := ss.writer.newWriteOp(domainID, domainAddress) +func (ss *stateStore) ResetTransaction(ctx context.Context, contractAddress, domainID string, transactionID uuid.UUID) error { + op := ss.writer.newWriteOp(domainID, contractAddress) op.transactionLockDeletes = []uuid.UUID{transactionID} ss.writer.queue(ctx, op) diff --git a/core/go/internal/statestore/state_writer.go b/core/go/internal/statestore/state_writer.go index b44fc182a..49340a6f2 100644 --- a/core/go/internal/statestore/state_writer.go +++ b/core/go/internal/statestore/state_writer.go @@ -86,10 +86,10 @@ func newStateWriter(bgCtx context.Context, ss *stateStore, conf *StateWriterConf return sw } -func (sw *stateWriter) newWriteOp(domainName, domainAddress string) *writeOperation { +func (sw *stateWriter) newWriteOp(domainName, contractAddress string) *writeOperation { return &writeOperation{ id: tktypes.ShortID(), - domainKey: domainName + ":" + domainAddress, + domainKey: domainName + ":" + contractAddress, done: make(chan error, 1), // 1 slot to ensure we don't block the writer } } diff --git a/core/go/internal/statestore/statestore.go b/core/go/internal/statestore/statestore.go index 712f70722..f8576818b 100644 --- a/core/go/internal/statestore/statestore.go +++ b/core/go/internal/statestore/statestore.go @@ -46,8 +46,8 @@ var StateWriterConfigDefaults = StateWriterConfig{ type StateStore interface { RPCModule() *rpcserver.RPCModule - RunInDomainContext(domainName, domainAddress string, fn DomainContextFunction) error - RunInDomainContextFlush(domainName, domainAddress string, fn DomainContextFunction) error + RunInDomainContext(domainName, contractAddress string, fn DomainContextFunction) error + RunInDomainContextFlush(domainName, contractAddress string, fn DomainContextFunction) error EnsureABISchemas(ctx context.Context, domainName string, defs []*abi.Parameter) ([]Schema, error) Close() } diff --git a/core/go/internal/statestore/statestore_rpc.go b/core/go/internal/statestore/statestore_rpc.go index ecc032d7f..9f11d433d 100644 --- a/core/go/internal/statestore/statestore_rpc.go +++ b/core/go/internal/statestore/statestore_rpc.go @@ -46,12 +46,12 @@ func (ss *stateStore) rpcListSchema() rpcserver.RPCHandler { func (ss *stateStore) rpcStoreState() rpcserver.RPCHandler { return rpcserver.RPCMethod4(func(ctx context.Context, domain string, - domainAddress string, + contractAddress string, schema string, value tktypes.RawJSON, ) (*State, error) { var state *State - newState, err := ss.PersistState(ctx, domain, domainAddress, schema, value) + newState, err := ss.PersistState(ctx, domain, contractAddress, schema, value) if err == nil { state = newState.State } @@ -62,11 +62,11 @@ func (ss *stateStore) rpcStoreState() rpcserver.RPCHandler { func (ss *stateStore) rpcQuery() rpcserver.RPCHandler { return rpcserver.RPCMethod5(func(ctx context.Context, domain string, - domainAddress string, + contractAddress string, schema string, query query.QueryJSON, status StateStatusQualifier, ) ([]*State, error) { - return ss.FindStates(ctx, domain, domainAddress, schema, &query, status) + return ss.FindStates(ctx, domain, contractAddress, schema, &query, status) }) } diff --git a/core/go/pkg/testbed/ut_simdomain_notarized_token_test.go b/core/go/pkg/testbed/ut_simdomain_notarized_token_test.go index cc3ac59a2..f4bfc405f 100644 --- a/core/go/pkg/testbed/ut_simdomain_notarized_token_test.go +++ b/core/go/pkg/testbed/ut_simdomain_notarized_token_test.go @@ -161,9 +161,9 @@ func TestDemoNotarizedCoinSelection(t *testing.T) { } } res, err := callbacks.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - DomainAddress: contractAddr, - SchemaId: fakeCoinSchemaID, - QueryJson: tktypes.JSONString(jq).String(), + ContractAddress: contractAddr, + SchemaId: fakeCoinSchemaID, + QueryJson: tktypes.JSONString(jq).String(), }) if err != nil { return nil, nil, nil, err diff --git a/domains/noto/internal/noto/noto.go b/domains/noto/internal/noto/noto.go index 208eb42af..c356af17d 100644 --- a/domains/noto/internal/noto/noto.go +++ b/domains/noto/internal/noto/noto.go @@ -327,8 +327,8 @@ func (n *Noto) gatherCoins(ctx context.Context, inputs, outputs []*pb.Endorsable }, nil } -func (n *Noto) FindCoins(ctx context.Context, domainAddress, query string) ([]*types.NotoCoin, error) { - states, err := n.findAvailableStates(ctx, domainAddress, query) +func (n *Noto) FindCoins(ctx context.Context, contractAddress, query string) ([]*types.NotoCoin, error) { + states, err := n.findAvailableStates(ctx, contractAddress, query) if err != nil { return nil, err } diff --git a/domains/noto/internal/noto/states.go b/domains/noto/internal/noto/states.go index 2cb85dbf4..41b254038 100644 --- a/domains/noto/internal/noto/states.go +++ b/domains/noto/internal/noto/states.go @@ -83,7 +83,7 @@ func (n *Noto) makeNewState(coin *types.NotoCoin) (*pb.NewState, error) { }, nil } -func (n *Noto) prepareInputs(ctx context.Context, domainAddress string, owner ethtypes.Address0xHex, amount *ethtypes.HexInteger) ([]*types.NotoCoin, []*pb.StateRef, *big.Int, error) { +func (n *Noto) prepareInputs(ctx context.Context, contractAddress string, owner ethtypes.Address0xHex, amount *ethtypes.HexInteger) ([]*types.NotoCoin, []*pb.StateRef, *big.Int, error) { var lastStateTimestamp int64 total := big.NewInt(0) stateRefs := []*pb.StateRef{} @@ -99,7 +99,7 @@ func (n *Noto) prepareInputs(ctx context.Context, domainAddress string, owner et } log.L(ctx).Debugf("State query: %s", queryBuilder.Query()) - states, err := n.findAvailableStates(ctx, domainAddress, queryBuilder.Query().String()) + states, err := n.findAvailableStates(ctx, contractAddress, queryBuilder.Query().String()) if err != nil { return nil, nil, nil, err @@ -139,11 +139,11 @@ func (n *Noto) prepareOutputs(owner ethtypes.Address0xHex, amount *ethtypes.HexI return []*types.NotoCoin{newCoin}, []*pb.NewState{newState}, err } -func (n *Noto) findAvailableStates(ctx context.Context, domainAddress, query string) ([]*pb.StoredState, error) { +func (n *Noto) findAvailableStates(ctx context.Context, contractAddress, query string) ([]*pb.StoredState, error) { req := &pb.FindAvailableStatesRequest{ - DomainAddress: domainAddress, - SchemaId: n.coinSchema.Id, - QueryJson: query, + ContractAddress: contractAddress, + SchemaId: n.coinSchema.Id, + QueryJson: query, } res, err := n.Callbacks.FindAvailableStates(ctx, req) if err != nil { diff --git a/domains/noto/pkg/noto/noto.go b/domains/noto/pkg/noto/noto.go index e6e0419a8..7767fa9d8 100644 --- a/domains/noto/pkg/noto/noto.go +++ b/domains/noto/pkg/noto/noto.go @@ -26,7 +26,7 @@ import ( type Noto interface { plugintk.DomainAPI GetHandler(method string) types.DomainHandler - FindCoins(ctx context.Context, domainAddress, query string) ([]*types.NotoCoin, error) + FindCoins(ctx context.Context, contractAddress, query string) ([]*types.NotoCoin, error) } func New(callbacks plugintk.DomainCallbacks) Noto { diff --git a/domains/pente/src/main/java/io/kaleido/paladin/pente/domain/PenteDomain.java b/domains/pente/src/main/java/io/kaleido/paladin/pente/domain/PenteDomain.java index 7ba76e0a8..bef7328d8 100644 --- a/domains/pente/src/main/java/io/kaleido/paladin/pente/domain/PenteDomain.java +++ b/domains/pente/src/main/java/io/kaleido/paladin/pente/domain/PenteDomain.java @@ -357,10 +357,10 @@ protected CompletableFuture prepareTransact /** during assembly we load available states from the Paladin state store */ class AssemblyAccountLoader implements AccountLoader { private final HashMap loadedAccountStates = new HashMap<>(); - private final String domainAddress; + private final String contractAddress; - AssemblyAccountLoader(String domainAddress) { - this.domainAddress = domainAddress; + AssemblyAccountLoader(String contractAddress) { + this.contractAddress = contractAddress; } public Optional load(org.hyperledger.besu.datatypes.Address address) throws IOException { @@ -370,7 +370,7 @@ public Optional load(org.hyperledger.besu.datatypes.Address ad isEqual("address", address.toString()). json(); var response = findAvailableStates(FromDomain.FindAvailableStatesRequest.newBuilder(). - setDomainAddress(this.domainAddress). + setContractAddress(this.contractAddress). setSchemaId(config.schemaId_AccountStateLatest()). setQueryJson(queryJson). build()).get(); diff --git a/domains/zeto/internal/zeto/states.go b/domains/zeto/internal/zeto/states.go index 1015aafb1..8d3b1b4e6 100644 --- a/domains/zeto/internal/zeto/states.go +++ b/domains/zeto/internal/zeto/states.go @@ -50,7 +50,7 @@ func (z *Zeto) makeNewState(coin *types.ZetoCoin) (*pb.NewState, error) { }, nil } -func (z *Zeto) prepareInputs(ctx context.Context, domainAddress, owner string, amount *ethtypes.HexInteger) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, error) { +func (z *Zeto) prepareInputs(ctx context.Context, contractAddress, owner string, amount *ethtypes.HexInteger) ([]*types.ZetoCoin, []*pb.StateRef, *big.Int, error) { var lastStateTimestamp int64 total := big.NewInt(0) stateRefs := []*pb.StateRef{} @@ -64,7 +64,7 @@ func (z *Zeto) prepareInputs(ctx context.Context, domainAddress, owner string, a if lastStateTimestamp > 0 { queryBuilder.GreaterThan(".created", lastStateTimestamp) } - states, err := z.findAvailableStates(ctx, domainAddress, queryBuilder.Query().String()) + states, err := z.findAvailableStates(ctx, contractAddress, queryBuilder.Query().String()) if err != nil { return nil, nil, nil, err } @@ -126,11 +126,11 @@ func (z *Zeto) prepareOutputs(owner string, ownerKey *babyjub.PublicKey, amount return []*types.ZetoCoin{newCoin}, []*pb.NewState{newState}, err } -func (z *Zeto) findAvailableStates(ctx context.Context, domainAddress, query string) ([]*pb.StoredState, error) { +func (z *Zeto) findAvailableStates(ctx context.Context, contractAddress, query string) ([]*pb.StoredState, error) { req := &pb.FindAvailableStatesRequest{ - DomainAddress: domainAddress, - SchemaId: z.coinSchema.Id, - QueryJson: query, + ContractAddress: contractAddress, + SchemaId: z.coinSchema.Id, + QueryJson: query, } res, err := z.Callbacks.FindAvailableStates(ctx, req) if err != nil { diff --git a/domains/zeto/internal/zeto/zeto.go b/domains/zeto/internal/zeto/zeto.go index ceecb5646..f4f91272d 100644 --- a/domains/zeto/internal/zeto/zeto.go +++ b/domains/zeto/internal/zeto/zeto.go @@ -241,8 +241,8 @@ func (z *Zeto) validateTransaction(ctx context.Context, tx *pb.TransactionSpecif }, handler, nil } -func (z *Zeto) FindCoins(ctx context.Context, domainAddress, query string) ([]*types.ZetoCoin, error) { - states, err := z.findAvailableStates(ctx, domainAddress, query) +func (z *Zeto) FindCoins(ctx context.Context, contractAddress, query string) ([]*types.ZetoCoin, error) { + states, err := z.findAvailableStates(ctx, contractAddress, query) if err != nil { return nil, err } diff --git a/domains/zeto/pkg/zeto/zeto.go b/domains/zeto/pkg/zeto/zeto.go index 3ff7396fe..0c5bacb8d 100644 --- a/domains/zeto/pkg/zeto/zeto.go +++ b/domains/zeto/pkg/zeto/zeto.go @@ -26,7 +26,7 @@ import ( type Zeto interface { plugintk.DomainAPI GetHandler(method string) types.DomainHandler - FindCoins(ctx context.Context, domainAddress, query string) ([]*types.ZetoCoin, error) + FindCoins(ctx context.Context, contractAddress, query string) ([]*types.ZetoCoin, error) } func New(callbacks plugintk.DomainCallbacks) Zeto { diff --git a/toolkit/proto/protos/from_domain.proto b/toolkit/proto/protos/from_domain.proto index 46a887cc1..7f19e437b 100644 --- a/toolkit/proto/protos/from_domain.proto +++ b/toolkit/proto/protos/from_domain.proto @@ -18,7 +18,7 @@ syntax = "proto3"; package github.com.kaleido_io.paladin.toolkit; message FindAvailableStatesRequest { - string domain_address = 1; // The address of the domain instance + string contract_address = 1; // The private smart contract address string schema_id = 2; // The ID of the schema string query_json = 3; // The query specification in JSON } From 40bd60ac45a49e67f5cd3ae2496d9a33cd6245ba Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 14:30:34 -0400 Subject: [PATCH 13/26] Rename DomainID -> DomainName Signed-off-by: Andrew Richardson --- .../000001_create_transactions_table.up.sql | 2 +- .../000002_create_schemas_table.up.sql | 4 +- .../000003_create_states_table.up.sql | 5 ++- .../000001_create_transactions_table.up.sql | 2 +- .../sqlite/000002_create_schemas_table.up.sql | 4 +- .../sqlite/000003_create_states_table.up.sql | 5 ++- core/go/internal/statestore/abi_schema.go | 10 ++--- .../go/internal/statestore/abi_schema_test.go | 12 +++--- core/go/internal/statestore/schema.go | 24 ++++++------ core/go/internal/statestore/state.go | 38 +++++++++---------- .../internal/statestore/state_status_test.go | 4 +- .../statestore/statestore_rpc_test.go | 2 +- .../transactionstore/transactionstore.go | 5 ++- domains/noto/internal/noto/noto.go | 2 - domains/zeto/internal/zeto/zeto.go | 2 - 15 files changed, 60 insertions(+), 61 deletions(-) diff --git a/core/go/db/migrations/postgres/000001_create_transactions_table.up.sql b/core/go/db/migrations/postgres/000001_create_transactions_table.up.sql index f2d503dc1..eccf0a346 100644 --- a/core/go/db/migrations/postgres/000001_create_transactions_table.up.sql +++ b/core/go/db/migrations/postgres/000001_create_transactions_table.up.sql @@ -7,7 +7,7 @@ CREATE TABLE transactions ( contract TEXT NOT NULL, "from" TEXT NOT NULL, sequence_id UUID, - domain_id TEXT, + domain_name TEXT, schema_id TEXT, assembled_round BIGINT, attestation_plan TEXT, diff --git a/core/go/db/migrations/postgres/000002_create_schemas_table.up.sql b/core/go/db/migrations/postgres/000002_create_schemas_table.up.sql index d1cbc7b57..077dc3b7d 100644 --- a/core/go/db/migrations/postgres/000002_create_schemas_table.up.sql +++ b/core/go/db/migrations/postgres/000002_create_schemas_table.up.sql @@ -2,11 +2,11 @@ BEGIN; CREATE TABLE schemas ( "id" TEXT NOT NULL, "created_at" BIGINT, - "domain_id" TEXT, + "domain_name" TEXT, "type" TEXT, "signature" TEXT, "definition" TEXT, "labels" TEXT, - PRIMARY KEY ("domain_id", "id") + PRIMARY KEY ("domain_name", "id") ); COMMIT; \ No newline at end of file diff --git a/core/go/db/migrations/postgres/000003_create_states_table.up.sql b/core/go/db/migrations/postgres/000003_create_states_table.up.sql index 92e4a2239..d083f4ba3 100644 --- a/core/go/db/migrations/postgres/000003_create_states_table.up.sql +++ b/core/go/db/migrations/postgres/000003_create_states_table.up.sql @@ -3,13 +3,14 @@ BEGIN; CREATE TABLE states ( "id" TEXT NOT NULL, "created_at" BIGINT NOT NULL, - "domain_id" TEXT, + "domain_name" TEXT, "schema" TEXT, "contract_address" TEXT, "data" TEXT, PRIMARY KEY ("id"), - FOREIGN KEY ("domain_id", "schema") REFERENCES schemas ("domain_id", "id") ON DELETE CASCADE + FOREIGN KEY ("domain_name", "schema") REFERENCES schemas ("domain_name", "id") ON DELETE CASCADE ); +CREATE INDEX states_by_domain ON states("domain_name", "schema", "contract_address"); CREATE TABLE state_labels ( "state" TEXT NOT NULL, diff --git a/core/go/db/migrations/sqlite/000001_create_transactions_table.up.sql b/core/go/db/migrations/sqlite/000001_create_transactions_table.up.sql index 4ae46d2af..fda091c76 100644 --- a/core/go/db/migrations/sqlite/000001_create_transactions_table.up.sql +++ b/core/go/db/migrations/sqlite/000001_create_transactions_table.up.sql @@ -6,7 +6,7 @@ CREATE TABLE transactions ( contract VARCHAR NOT NULL, "from" VARCHAR NOT NULL, sequence_id UUID, - domain_id VARCHAR, + domain_name VARCHAR, schema_id VARCHAR, payload_json VARCHAR, payload_rlp VARCHAR, diff --git a/core/go/db/migrations/sqlite/000002_create_schemas_table.up.sql b/core/go/db/migrations/sqlite/000002_create_schemas_table.up.sql index f22205aa5..161afa4c9 100644 --- a/core/go/db/migrations/sqlite/000002_create_schemas_table.up.sql +++ b/core/go/db/migrations/sqlite/000002_create_schemas_table.up.sql @@ -1,10 +1,10 @@ CREATE TABLE schemas ( "id" VARCHAR NOT NULL, "created_at" BIGINT, - "domain_id" VARCHAR, + "domain_name" VARCHAR, "type" VARCHAR, "signature" VARCHAR, "definition" VARCHAR, "labels" VARCHAR, - PRIMARY KEY ("domain_id", "id") + PRIMARY KEY ("domain_name", "id") ); diff --git a/core/go/db/migrations/sqlite/000003_create_states_table.up.sql b/core/go/db/migrations/sqlite/000003_create_states_table.up.sql index 546eff023..ef76ef142 100644 --- a/core/go/db/migrations/sqlite/000003_create_states_table.up.sql +++ b/core/go/db/migrations/sqlite/000003_create_states_table.up.sql @@ -1,13 +1,14 @@ CREATE TABLE states ( "id" VARCHAR NOT NULL, "created_at" BIGINT NOT NULL, - "domain_id" VARCHAR, + "domain_name" VARCHAR, "schema" VARCHAR, "contract_address" VARCHAR, "data" VARCHAR, PRIMARY KEY ("id"), - FOREIGN KEY ("domain_id", "schema") REFERENCES schemas ("domain_id", "id") ON DELETE CASCADE + FOREIGN KEY ("domain_name", "schema") REFERENCES schemas ("domain_name", "id") ON DELETE CASCADE ); +CREATE INDEX states_by_domain ON states("domain_name", "schema", "contract_address"); CREATE TABLE state_labels ( "state" VARCHAR NOT NULL, diff --git a/core/go/internal/statestore/abi_schema.go b/core/go/internal/statestore/abi_schema.go index f4b747e51..74dca65bc 100644 --- a/core/go/internal/statestore/abi_schema.go +++ b/core/go/internal/statestore/abi_schema.go @@ -42,12 +42,12 @@ type abiSchema struct { abiLabelInfo []*schemaLabelInfo } -func newABISchema(ctx context.Context, domainID string, def *abi.Parameter) (*abiSchema, error) { +func newABISchema(ctx context.Context, domainName string, def *abi.Parameter) (*abiSchema, error) { as := &abiSchema{ SchemaPersisted: &SchemaPersisted{ - DomainID: domainID, - Type: SchemaTypeABI, - Labels: []string{}, + DomainName: domainName, + Type: SchemaTypeABI, + Labels: []string{}, }, definition: def, } @@ -357,7 +357,7 @@ func (as *abiSchema) ProcessState(ctx context.Context, data tktypes.RawJSON) (*S State: &State{ ID: hashID, CreatedAt: now, - DomainID: as.DomainID, + DomainName: as.DomainName, Schema: as.ID, Data: jsonData, Labels: psd.labels, diff --git a/core/go/internal/statestore/abi_schema_test.go b/core/go/internal/statestore/abi_schema_test.go index 18eb1f453..54c1ca1df 100644 --- a/core/go/internal/statestore/abi_schema_test.go +++ b/core/go/internal/statestore/abi_schema_test.go @@ -99,7 +99,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { assert.NotNil(t, as.definition) assert.Equal(t, "type=MyStruct(uint256 field1,string field2,int64 field3,bool field4,address field5,int256 field6,bytes field7,uint32 field8,string field9),labels=[field1,field2,field3,field4,field5,field6,field7,field8]", as.Persisted().Signature) cacheKey := "domain1/0xcf41493c8bb9652d1483ee6cb5122efbec6fbdf67cc27363ba5b030b59244cad" - assert.Equal(t, cacheKey, schemaCacheKey(as.Persisted().DomainID, as.Persisted().ID)) + assert.Equal(t, cacheKey, schemaCacheKey(as.Persisted().DomainName, as.Persisted().ID)) err = ss.persistSchemas([]*SchemaPersisted{as.SchemaPersisted}) require.NoError(t, err) @@ -162,7 +162,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { schemaID = as.IDString() getValidate := func() { - as1, err := ss.GetSchema(ctx, as.Persisted().DomainID, schemaID, true) + as1, err := ss.GetSchema(ctx, as.Persisted().DomainName, schemaID, true) require.NoError(t, err) assert.NotNil(t, as1) as1Sig, err := as1.(*abiSchema).FullSignature(ctx) @@ -181,7 +181,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { getValidate() // Get the state back too - state1a, err := ss.GetState(ctx, as.Persisted().DomainID, state1.ID.String(), true, true) + state1a, err := ss.GetState(ctx, as.Persisted().DomainName, state1.ID.String(), true, true) require.NoError(t, err) assert.Equal(t, state1.State, state1a) @@ -200,7 +200,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err := ss.FindStates(ctx, as.Persisted().DomainID, "0x1234", schemaID, query, "all") + states, err := ss.FindStates(ctx, as.Persisted().DomainName, "0x1234", schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 1) @@ -211,7 +211,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err = ss.FindStates(ctx, as.Persisted().DomainID, "0x1234", schemaID, query, "all") + states, err = ss.FindStates(ctx, as.Persisted().DomainName, "0x1234", schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 0) @@ -222,7 +222,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err = ss.FindStates(ctx, as.Persisted().DomainID, "0x1234", schemaID, query, "all") + states, err = ss.FindStates(ctx, as.Persisted().DomainName, "0x1234", schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 0) } diff --git a/core/go/internal/statestore/schema.go b/core/go/internal/statestore/schema.go index cbe5fc452..059be10c5 100644 --- a/core/go/internal/statestore/schema.go +++ b/core/go/internal/statestore/schema.go @@ -49,7 +49,7 @@ const ( type SchemaPersisted struct { ID tktypes.Bytes32 `json:"id" gorm:"primaryKey"` CreatedAt tktypes.Timestamp `json:"created" gorm:"autoCreateTime:false"` // we calculate the created time ourselves due to complex in-memory caching - DomainID string `json:"domain"` + DomainName string `json:"domain"` Type SchemaType `json:"type"` Signature string `json:"signature"` Definition tktypes.RawJSON `json:"definition"` @@ -80,8 +80,8 @@ type labelInfoAccess interface { labelInfo() []*schemaLabelInfo } -func schemaCacheKey(domainID string, id tktypes.Bytes32) string { - return domainID + "/" + id.String() +func schemaCacheKey(domainName string, id tktypes.Bytes32) string { + return domainName + "/" + id.String() } func (ss *stateStore) persistSchemas(schemas []*SchemaPersisted) error { @@ -89,7 +89,7 @@ func (ss *stateStore) persistSchemas(schemas []*SchemaPersisted) error { Table("schemas"). Clauses(clause.OnConflict{ Columns: []clause.Column{ - {Name: "domain_id"}, + {Name: "domain_name"}, {Name: "id"}, }, DoNothing: true, // immutable @@ -98,17 +98,17 @@ func (ss *stateStore) persistSchemas(schemas []*SchemaPersisted) error { Error } -func (ss *stateStore) GetSchema(ctx context.Context, domainID, schemaID string, failNotFound bool) (Schema, error) { +func (ss *stateStore) GetSchema(ctx context.Context, domainName, schemaID string, failNotFound bool) (Schema, error) { id, err := tktypes.ParseBytes32Ctx(ctx, schemaID) if err != nil { return nil, err } - return ss.getSchemaByID(ctx, domainID, id, failNotFound) + return ss.getSchemaByID(ctx, domainName, id, failNotFound) } -func (ss *stateStore) getSchemaByID(ctx context.Context, domainID string, schemaID tktypes.Bytes32, failNotFound bool) (Schema, error) { +func (ss *stateStore) getSchemaByID(ctx context.Context, domainName string, schemaID tktypes.Bytes32, failNotFound bool) (Schema, error) { - cacheKey := schemaCacheKey(domainID, schemaID) + cacheKey := schemaCacheKey(domainName, schemaID) s, cached := ss.abiSchemaCache.Get(cacheKey) if cached { return s, nil @@ -117,7 +117,7 @@ func (ss *stateStore) getSchemaByID(ctx context.Context, domainID string, schema var results []*SchemaPersisted err := ss.p.DB(). Table("schemas"). - Where("domain_id = ?", domainID). + Where("domain_name = ?", domainName). Where("id = ?", schemaID). Limit(1). Find(&results). @@ -143,12 +143,12 @@ func (ss *stateStore) getSchemaByID(ctx context.Context, domainID string, schema return s, nil } -func (ss *stateStore) ListSchemas(ctx context.Context, domainID string) (results []Schema, err error) { +func (ss *stateStore) ListSchemas(ctx context.Context, domainName string) (results []Schema, err error) { var ids []*idOnly err = ss.p.DB(). Table("schemas"). Select("id"). - Where("domain_id = ?", domainID). + Where("domain_name = ?", domainName). Find(&ids). Error if err != nil { @@ -156,7 +156,7 @@ func (ss *stateStore) ListSchemas(ctx context.Context, domainID string) (results } results = make([]Schema, len(ids)) for i, id := range ids { - if results[i], err = ss.getSchemaByID(ctx, domainID, id.ID, true); err != nil { + if results[i], err = ss.getSchemaByID(ctx, domainName, id.ID, true); err != nil { return nil, err } } diff --git a/core/go/internal/statestore/state.go b/core/go/internal/statestore/state.go index b97fa59eb..5729044ad 100644 --- a/core/go/internal/statestore/state.go +++ b/core/go/internal/statestore/state.go @@ -32,7 +32,7 @@ import ( type State struct { ID tktypes.Bytes32 `json:"id" gorm:"primaryKey"` CreatedAt tktypes.Timestamp `json:"created" gorm:"autoCreateTime:nano"` - DomainID string `json:"domain"` + DomainName string `json:"domain"` Schema tktypes.Bytes32 `json:"schema"` ContractAddress string `json:"contractAddress"` Data tktypes.RawJSON `json:"data"` @@ -77,9 +77,9 @@ func (s *StateWithLabels) ValueSet() filters.ValueSet { return s.LabelValues } -func (ss *stateStore) PersistState(ctx context.Context, domainID, contractAddress, schemaID string, data tktypes.RawJSON) (*StateWithLabels, error) { +func (ss *stateStore) PersistState(ctx context.Context, domainName, contractAddress, schemaID string, data tktypes.RawJSON) (*StateWithLabels, error) { - schema, err := ss.GetSchema(ctx, domainID, schemaID, true) + schema, err := ss.GetSchema(ctx, domainName, schemaID, true) if err != nil { return nil, err } @@ -90,13 +90,13 @@ func (ss *stateStore) PersistState(ctx context.Context, domainID, contractAddres } s.ContractAddress = contractAddress - op := ss.writer.newWriteOp(s.State.DomainID, contractAddress) + op := ss.writer.newWriteOp(s.State.DomainName, contractAddress) op.states = []*StateWithLabels{s} ss.writer.queue(ctx, op) return s, op.flush(ctx) } -func (ss *stateStore) GetState(ctx context.Context, domainID, stateID string, failNotFound, withLabels bool) (*State, error) { +func (ss *stateStore) GetState(ctx context.Context, domainName, stateID string, failNotFound, withLabels bool) (*State, error) { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return nil, err @@ -108,7 +108,7 @@ func (ss *stateStore) GetState(ctx context.Context, domainID, stateID string, fa } var states []*State err = q. - Where("domain_id = ?", domainID). + Where("domain_name = ?", domainName). Where("id = ?", id). Limit(1). Find(&states). @@ -158,17 +158,17 @@ func (ss *stateStore) labelSetFor(schema Schema) *trackingLabelSet { return &tls } -func (ss *stateStore) FindStates(ctx context.Context, domainID, contractAddress, schemaID string, query *query.QueryJSON, status StateStatusQualifier) (s []*State, err error) { - _, s, err = ss.findStates(ctx, domainID, contractAddress, schemaID, query, status) +func (ss *stateStore) FindStates(ctx context.Context, domainName, contractAddress, schemaID string, query *query.QueryJSON, status StateStatusQualifier) (s []*State, err error) { + _, s, err = ss.findStates(ctx, domainName, contractAddress, schemaID, query, status) return s, err } -func (ss *stateStore) findStates(ctx context.Context, domainID, contractAddress, schemaID string, jq *query.QueryJSON, status StateStatusQualifier, excluded ...*idOnly) (schema Schema, s []*State, err error) { +func (ss *stateStore) findStates(ctx context.Context, domainName, contractAddress, schemaID string, jq *query.QueryJSON, status StateStatusQualifier, excluded ...*idOnly) (schema Schema, s []*State, err error) { if len(jq.Sort) == 0 { jq.Sort = []string{".created"} } - schema, err = ss.GetSchema(ctx, domainID, schemaID, true) + schema, err = ss.GetSchema(ctx, domainName, schemaID, true) if err != nil { return nil, nil, err } @@ -194,7 +194,7 @@ func (ss *stateStore) findStates(ctx context.Context, domainID, contractAddress, q = q.Joins("Confirmed", db.Select("transaction")). Joins("Spent", db.Select("transaction")). Joins("Locked", db.Select("transaction")). - Where("domain_id = ?", domainID). + Where("domain_name = ?", domainName). Where("contract_address = ?", contractAddress). Where("schema = ?", schema.Persisted().ID) @@ -213,13 +213,13 @@ func (ss *stateStore) findStates(ctx context.Context, domainID, contractAddress, return schema, states, nil } -func (ss *stateStore) MarkConfirmed(ctx context.Context, domainID, contractAddress, stateID string, transactionID uuid.UUID) error { +func (ss *stateStore) MarkConfirmed(ctx context.Context, domainName, contractAddress, stateID string, transactionID uuid.UUID) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID, contractAddress) + op := ss.writer.newWriteOp(domainName, contractAddress) op.stateConfirms = []*StateConfirm{ {State: id, Transaction: transactionID}, } @@ -228,13 +228,13 @@ func (ss *stateStore) MarkConfirmed(ctx context.Context, domainID, contractAddre return op.flush(ctx) } -func (ss *stateStore) MarkSpent(ctx context.Context, domainID, contractAddress, stateID string, transactionID uuid.UUID) error { +func (ss *stateStore) MarkSpent(ctx context.Context, domainName, contractAddress, stateID string, transactionID uuid.UUID) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID, contractAddress) + op := ss.writer.newWriteOp(domainName, contractAddress) op.stateSpends = []*StateSpend{ {State: id, Transaction: transactionID}, } @@ -243,13 +243,13 @@ func (ss *stateStore) MarkSpent(ctx context.Context, domainID, contractAddress, return op.flush(ctx) } -func (ss *stateStore) MarkLocked(ctx context.Context, domainID, contractAddress, stateID string, transactionID uuid.UUID, creating, spending bool) error { +func (ss *stateStore) MarkLocked(ctx context.Context, domainName, contractAddress, stateID string, transactionID uuid.UUID, creating, spending bool) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err } - op := ss.writer.newWriteOp(domainID, contractAddress) + op := ss.writer.newWriteOp(domainName, contractAddress) op.stateLocks = []*StateLock{ {State: id, Transaction: transactionID, Creating: creating, Spending: spending}, } @@ -258,8 +258,8 @@ func (ss *stateStore) MarkLocked(ctx context.Context, domainID, contractAddress, return op.flush(ctx) } -func (ss *stateStore) ResetTransaction(ctx context.Context, contractAddress, domainID string, transactionID uuid.UUID) error { - op := ss.writer.newWriteOp(domainID, contractAddress) +func (ss *stateStore) ResetTransaction(ctx context.Context, domainName, contractAddress string, transactionID uuid.UUID) error { + op := ss.writer.newWriteOp(domainName, contractAddress) op.transactionLockDeletes = []uuid.UUID{transactionID} ss.writer.queue(ctx, op) diff --git a/core/go/internal/statestore/state_status_test.go b/core/go/internal/statestore/state_status_test.go index b1b21d1a8..6eaf82dee 100644 --- a/core/go/internal/statestore/state_status_test.go +++ b/core/go/internal/statestore/state_status_test.go @@ -54,7 +54,7 @@ const widgetABI = `{ ] }` -func makeWidgets(t *testing.T, ctx context.Context, ss *stateStore, domainID, schemaID string, withoutSalt []string) []*StateWithLabels { +func makeWidgets(t *testing.T, ctx context.Context, ss *stateStore, domainName, schemaID string, withoutSalt []string) []*StateWithLabels { states := make([]*StateWithLabels, len(withoutSalt)) for i, w := range withoutSalt { var ij map[string]interface{} @@ -63,7 +63,7 @@ func makeWidgets(t *testing.T, ctx context.Context, ss *stateStore, domainID, sc ij["salt"] = tktypes.RandHex(32) withSalt, err := json.Marshal(ij) require.NoError(t, err) - states[i], err = ss.PersistState(ctx, domainID, "0x1234", schemaID, withSalt) + states[i], err = ss.PersistState(ctx, domainName, "0x1234", schemaID, withSalt) require.NoError(t, err) fmt.Printf("widget[%d]: %s\n", i, states[i].Data) } diff --git a/core/go/internal/statestore/statestore_rpc_test.go b/core/go/internal/statestore/statestore_rpc_test.go index 689539411..4f4900f6a 100644 --- a/core/go/internal/statestore/statestore_rpc_test.go +++ b/core/go/internal/statestore/statestore_rpc_test.go @@ -91,7 +91,7 @@ func TestRPC(t *testing.T) { jsonTestLog(t, "pstate_storeState", state) assert.Nil(t, rpcErr) assert.Equal(t, schemas[0].ID, state.Schema) - assert.Equal(t, "domain1", state.DomainID) + assert.Equal(t, "domain1", state.DomainName) assert.Equal(t, "0x30e278bca8d876cdceb24520b0ebe736a64a9cb8019157f40fa5b03f083f824d", state.ID.String()) var states []*State diff --git a/core/go/internal/transactionstore/transactionstore.go b/core/go/internal/transactionstore/transactionstore.go index 4ebe2e888..51566b75d 100644 --- a/core/go/internal/transactionstore/transactionstore.go +++ b/core/go/internal/transactionstore/transactionstore.go @@ -74,7 +74,7 @@ type Transaction struct { ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"` From string `gorm:"type:text"` SequenceID *uuid.UUID `gorm:"type:uuid"` - DomainID string `gorm:"type:uuid"` + DomainName string `gorm:"type:text"` SchemaID string `gorm:"type:uuid"` AssembledRound int64 `gorm:"type:int"` AttestationPlan string `gorm:"type:text[]; serializer:json"` @@ -197,8 +197,9 @@ func (t *TransactionWrapper) GetAttestationResults(ctx context.Context) []*proto _ = json.Unmarshal([]byte(t.AttestationResults), &attResults) return attResults } + func (t *TransactionWrapper) GetDomainID(ctx context.Context) string { - return t.DomainID + return t.DomainName } func (t *TransactionWrapper) GetSchemaID(ctx context.Context) string { diff --git a/domains/noto/internal/noto/noto.go b/domains/noto/internal/noto/noto.go index c356af17d..f16cb2f30 100644 --- a/domains/noto/internal/noto/noto.go +++ b/domains/noto/internal/noto/noto.go @@ -48,7 +48,6 @@ type Noto struct { config types.DomainConfig chainID int64 - domainID string coinSchema *pb.StateSchema factoryABI abi.ABI contractABI abi.ABI @@ -99,7 +98,6 @@ func (n *Noto) ConfigureDomain(ctx context.Context, req *pb.ConfigureDomainReque } func (n *Noto) InitDomain(ctx context.Context, req *pb.InitDomainRequest) (*pb.InitDomainResponse, error) { - n.domainID = req.DomainUuid n.coinSchema = req.AbiStateSchemas[0] return &pb.InitDomainResponse{}, nil } diff --git a/domains/zeto/internal/zeto/zeto.go b/domains/zeto/internal/zeto/zeto.go index f4f91272d..dde62ebdc 100644 --- a/domains/zeto/internal/zeto/zeto.go +++ b/domains/zeto/internal/zeto/zeto.go @@ -39,7 +39,6 @@ type Zeto struct { config *types.DomainFactoryConfig chainID int64 - domainID string coinSchema *pb.StateSchema factoryABI abi.ABI } @@ -79,7 +78,6 @@ func (z *Zeto) ConfigureDomain(ctx context.Context, req *pb.ConfigureDomainReque } func (z *Zeto) InitDomain(ctx context.Context, req *pb.InitDomainRequest) (*pb.InitDomainResponse, error) { - z.domainID = req.DomainUuid z.coinSchema = req.AbiStateSchemas[0] return &pb.InitDomainResponse{}, nil } From a9b3f81cc043fd422eb0a04f119eff13ad7705d3 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 14:40:43 -0400 Subject: [PATCH 14/26] Remove domain ID Signed-off-by: Andrew Richardson --- core/go/internal/components/domainmgr.go | 2 +- core/go/internal/domainmgr/domain.go | 6 +---- core/go/internal/domainmgr/domain_test.go | 9 ++------ core/go/internal/domainmgr/manager.go | 10 +++----- core/go/internal/domainmgr/manager_test.go | 2 +- core/go/internal/plugins/domains.go | 2 +- core/go/internal/plugins/domains_test.go | 19 ++++++---------- core/go/internal/plugins/plugin_base_test.go | 24 ++++++++++---------- toolkit/proto/protos/to_domain.proto | 3 +-- 9 files changed, 29 insertions(+), 48 deletions(-) diff --git a/core/go/internal/components/domainmgr.go b/core/go/internal/components/domainmgr.go index 6010948a2..5d81cba14 100644 --- a/core/go/internal/components/domainmgr.go +++ b/core/go/internal/components/domainmgr.go @@ -33,7 +33,7 @@ type DomainManagerToDomain interface { type DomainManager interface { ManagerLifecycle ConfiguredDomains() map[string]*PluginConfig - DomainRegistered(name string, id uuid.UUID, toDomain DomainManagerToDomain) (fromDomain plugintk.DomainCallbacks, err error) + DomainRegistered(name string, toDomain DomainManagerToDomain) (fromDomain plugintk.DomainCallbacks, err error) GetDomainByName(ctx context.Context, name string) (Domain, error) GetSmartContractByAddress(ctx context.Context, addr tktypes.EthAddress) (DomainSmartContract, error) WaitForDeploy(ctx context.Context, txID uuid.UUID) (DomainSmartContract, error) diff --git a/core/go/internal/domainmgr/domain.go b/core/go/internal/domainmgr/domain.go index 3c23de4d7..1be532264 100644 --- a/core/go/internal/domainmgr/domain.go +++ b/core/go/internal/domainmgr/domain.go @@ -21,7 +21,6 @@ import ( "sync" "sync/atomic" - "github.com/google/uuid" "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/pkg/abi" "github.com/hyperledger/firefly-signer/pkg/eip712" @@ -46,7 +45,6 @@ type domain struct { conf *DomainConfig dm *domainManager - id uuid.UUID name string api components.DomainManagerToDomain registryAddress *tktypes.EthAddress @@ -62,13 +60,12 @@ type domain struct { initDone chan struct{} } -func (dm *domainManager) newDomain(id uuid.UUID, name string, conf *DomainConfig, toDomain components.DomainManagerToDomain) *domain { +func (dm *domainManager) newDomain(name string, conf *DomainConfig, toDomain components.DomainManagerToDomain) *domain { d := &domain{ dm: dm, conf: conf, initRetry: retry.NewRetryIndefinite(&conf.Init.Retry), name: name, - id: id, api: toDomain, initDone: make(chan struct{}), registryAddress: tktypes.MustEthAddress(conf.RegistryAddress), // check earlier in startup @@ -117,7 +114,6 @@ func (d *domain) processDomainConfig(confRes *prototk.ConfigureDomainResponse) ( } } return &prototk.InitDomainRequest{ - DomainUuid: d.id.String(), AbiStateSchemas: schemasProto, }, nil } diff --git a/core/go/internal/domainmgr/domain_test.go b/core/go/internal/domainmgr/domain_test.go index dc2331ab5..e2a9d9994 100644 --- a/core/go/internal/domainmgr/domain_test.go +++ b/core/go/internal/domainmgr/domain_test.go @@ -171,8 +171,7 @@ func newTestDomain(t *testing.T, realDB bool, domainConfig *prototk.DomainConfig } func registerTestDomain(t *testing.T, dm *domainManager, tp *testPlugin) { - domainID := uuid.New() - _, err := dm.DomainRegistered("test1", domainID, tp) + _, err := dm.DomainRegistered("test1", tp) require.NoError(t, err) da, err := dm.GetDomainByName(context.Background(), "test1") @@ -238,9 +237,6 @@ func TestDoubleRegisterReplaces(t *testing.T) { byName, err := dm.GetDomainByName(ctx, "test1") require.NoError(t, err) assert.Same(t, tp1.d, byName) - byUUID := dm.domainsByID[tp1.d.id] - require.NoError(t, err) - assert.Same(t, tp1.d, byUUID) } @@ -288,8 +284,7 @@ func TestDomainConfigureFail(t *testing.T) { }, }) - domainID := uuid.New() - _, err := dm.DomainRegistered("test1", domainID, tp) + _, err := dm.DomainRegistered("test1", tp) require.NoError(t, err) da, err := dm.GetDomainByName(ctx, "test1") diff --git a/core/go/internal/domainmgr/manager.go b/core/go/internal/domainmgr/manager.go index 49452f50a..1c47505c3 100644 --- a/core/go/internal/domainmgr/manager.go +++ b/core/go/internal/domainmgr/manager.go @@ -58,7 +58,6 @@ func NewDomainManager(bgCtx context.Context, conf *DomainManagerConfig) componen return &domainManager{ bgCtx: bgCtx, conf: conf, - domainsByID: make(map[uuid.UUID]*domain), domainsByName: make(map[string]*domain), domainsByAddress: make(map[tktypes.EthAddress]*domain), contractWaiter: inflight.NewInflightManager[uuid.UUID, *PrivateSmartContract](uuid.Parse), @@ -76,7 +75,6 @@ type domainManager struct { blockIndexer blockindexer.BlockIndexer ethClientFactory ethclient.EthClientFactory - domainsByID map[uuid.UUID]*domain domainsByName map[string]*domain domainsByAddress map[tktypes.EthAddress]*domain @@ -123,7 +121,7 @@ func (dm *domainManager) Start() error { return nil } func (dm *domainManager) Stop() { dm.mux.Lock() var allDomains []*domain - for _, d := range dm.domainsByID { + for _, d := range dm.domainsByName { allDomains = append(allDomains, d) } dm.mux.Unlock() @@ -136,7 +134,6 @@ func (dm *domainManager) Stop() { func (dm *domainManager) cleanupDomain(d *domain) { // must not hold the domain lock when running this d.close() - delete(dm.domainsByID, d.id) delete(dm.domainsByName, d.name) delete(dm.domainsByAddress, *d.RegistryAddress()) } @@ -149,7 +146,7 @@ func (dm *domainManager) ConfiguredDomains() map[string]*components.PluginConfig return pluginConf } -func (dm *domainManager) DomainRegistered(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (fromDomain plugintk.DomainCallbacks, err error) { +func (dm *domainManager) DomainRegistered(name string, toDomain components.DomainManagerToDomain) (fromDomain plugintk.DomainCallbacks, err error) { dm.mux.Lock() defer dm.mux.Unlock() @@ -171,8 +168,7 @@ func (dm *domainManager) DomainRegistered(name string, id uuid.UUID, toDomain co } // Initialize - d := dm.newDomain(id, name, conf, toDomain) - dm.domainsByID[id] = d + d := dm.newDomain(name, conf, toDomain) dm.domainsByName[name] = d go d.init() return d, nil diff --git a/core/go/internal/domainmgr/manager_test.go b/core/go/internal/domainmgr/manager_test.go index 155836b97..989f2c9a5 100644 --- a/core/go/internal/domainmgr/manager_test.go +++ b/core/go/internal/domainmgr/manager_test.go @@ -147,7 +147,7 @@ func TestDomainRegisteredNotFound(t *testing.T) { }) defer done() - _, err := dm.DomainRegistered("unknown", uuid.New(), nil) + _, err := dm.DomainRegistered("unknown", nil) assert.Regexp(t, "PD011600", err) } diff --git a/core/go/internal/plugins/domains.go b/core/go/internal/plugins/domains.go index 0e785ea1d..3b84f3e23 100644 --- a/core/go/internal/plugins/domains.go +++ b/core/go/internal/plugins/domains.go @@ -35,7 +35,7 @@ func (pm *pluginManager) ConnectDomain(stream prototk.PluginController_ConnectDo pluginId: plugin.id.String(), toPlugin: toPlugin, } - br.manager, err = pm.domainManager.DomainRegistered(plugin.name, plugin.id, br) + br.manager, err = pm.domainManager.DomainRegistered(plugin.name, br) if err != nil { return nil, err } diff --git a/core/go/internal/plugins/domains_test.go b/core/go/internal/plugins/domains_test.go index 0e4eb976e..26a6b5dce 100644 --- a/core/go/internal/plugins/domains_test.go +++ b/core/go/internal/plugins/domains_test.go @@ -35,7 +35,7 @@ import ( type testDomainManager struct { domains map[string]plugintk.Plugin - domainRegistered func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (fromDomain plugintk.DomainCallbacks, err error) + domainRegistered func(name string, toDomain components.DomainManagerToDomain) (fromDomain plugintk.DomainCallbacks, err error) findAvailableStates func(context.Context, *prototk.FindAvailableStatesRequest) (*prototk.FindAvailableStatesResponse, error) encodeData func(context.Context, *prototk.EncodeDataRequest) (*prototk.EncodeDataResponse, error) recoverSigner func(context.Context, *prototk.RecoverSignerRequest) (*prototk.RecoverSignerResponse, error) @@ -74,9 +74,9 @@ func (tp *testDomainManager) mock(t *testing.T) *componentmocks.DomainManager { } } mdm.On("ConfiguredDomains").Return(pluginMap).Maybe() - mdr := mdm.On("DomainRegistered", mock.Anything, mock.Anything, mock.Anything).Maybe() + mdr := mdm.On("DomainRegistered", mock.Anything, mock.Anything).Maybe() mdr.Run(func(args mock.Arguments) { - m2p, err := tp.domainRegistered(args[0].(string), args[1].(uuid.UUID), args[2].(components.DomainManagerToDomain)) + m2p, err := tp.domainRegistered(args[0].(string), args[1].(components.DomainManagerToDomain)) mdr.Return(m2p, err) }) return mdm @@ -115,7 +115,6 @@ func TestDomainRequestsOK(t *testing.T) { waitForAPI := make(chan components.DomainManagerToDomain, 1) waitForCallbacks := make(chan plugintk.DomainCallbacks, 1) - var domainID string domainFunctions := &plugintk.DomainAPIFunctions{ ConfigureDomain: func(ctx context.Context, cdr *prototk.ConfigureDomainRequest) (*prototk.ConfigureDomainResponse, error) { assert.Equal(t, int64(12345), cdr.ChainId) @@ -128,7 +127,6 @@ func TestDomainRequestsOK(t *testing.T) { }, nil }, InitDomain: func(ctx context.Context, idr *prototk.InitDomainRequest) (*prototk.InitDomainResponse, error) { - assert.Equal(t, domainID, idr.DomainUuid) return &prototk.InitDomainResponse{}, nil }, InitDeploy: func(ctx context.Context, idr *prototk.InitDeployRequest) (*prototk.InitDeployResponse, error) { @@ -181,9 +179,8 @@ func TestDomainRequestsOK(t *testing.T) { }), }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { assert.Equal(t, "domain1", name) - domainID = id.String() waitForAPI <- toDomain return tdm, nil } @@ -224,9 +221,7 @@ func TestDomainRequestsOK(t *testing.T) { require.NoError(t, err) assert.Equal(t, prototk.BaseLedgerSubmitConfig_ENDORSER_SUBMISSION, cdr.DomainConfig.BaseLedgerSubmitConfig.SubmitMode) - _, err = domainAPI.InitDomain(ctx, &prototk.InitDomainRequest{ - DomainUuid: domainID, - }) + _, err = domainAPI.InitDomain(ctx, &prototk.InitDomainRequest{}) require.NoError(t, err) // This is the point the domain manager would call us to say the domain is initialized @@ -328,7 +323,7 @@ func TestDomainRegisterFail(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { return nil, fmt.Errorf("pop") } @@ -369,7 +364,7 @@ func TestFromDomainRequestBadReq(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { return tdm, nil } diff --git a/core/go/internal/plugins/plugin_base_test.go b/core/go/internal/plugins/plugin_base_test.go index 90ec6fc9a..558bd1c97 100644 --- a/core/go/internal/plugins/plugin_base_test.go +++ b/core/go/internal/plugins/plugin_base_test.go @@ -165,7 +165,7 @@ func TestPluginRequestsError(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { return tdm, nil } tdm.findAvailableStates = func(ctx context.Context, req *prototk.FindAvailableStatesRequest) (*prototk.FindAvailableStatesResponse, error) { @@ -194,7 +194,7 @@ func TestSenderErrorHandling(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { waitForRegister <- toDomain return tdm, nil } @@ -254,7 +254,7 @@ func TestDomainRequestsBadResponse(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { waitForRegister <- toDomain return tdm, nil } @@ -296,7 +296,7 @@ func TestDomainRequestsErrorWithMessage(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { waitForRegister <- toDomain return tdm, nil } @@ -337,7 +337,7 @@ func TestDomainRequestsErrorNoMessage(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { waitForRegister <- toDomain return tdm, nil } @@ -374,7 +374,7 @@ func TestReceiveAfterTimeout(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { waitForRegister <- toDomain return tdm, nil } @@ -401,7 +401,7 @@ func TestReceiveAfterTimeout(t *testing.T) { func TestDomainSendBeforeRegister(t *testing.T) { - waitForRegister := make(chan uuid.UUID, 1) + waitForRegister := make(chan bool, 1) tdm := &testDomainManager{ domains: map[string]plugintk.Plugin{ @@ -419,8 +419,8 @@ func TestDomainSendBeforeRegister(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { - waitForRegister <- id + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + waitForRegister <- true return tdm, nil } @@ -454,7 +454,7 @@ func TestDomainSendDoubleRegister(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { waitForRegister <- toDomain return tdm, nil } @@ -493,7 +493,7 @@ func TestDomainRegisterWrongID(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { return tdm, nil } @@ -562,7 +562,7 @@ func TestDomainSendResponseWrongID(t *testing.T) { }, }, } - tdm.domainRegistered = func(name string, id uuid.UUID, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { + tdm.domainRegistered = func(name string, toDomain components.DomainManagerToDomain) (plugintk.DomainCallbacks, error) { waitForRegister <- toDomain return tdm, nil } diff --git a/toolkit/proto/protos/to_domain.proto b/toolkit/proto/protos/to_domain.proto index 371e0b646..517b38c7e 100644 --- a/toolkit/proto/protos/to_domain.proto +++ b/toolkit/proto/protos/to_domain.proto @@ -31,8 +31,7 @@ message ConfigureDomainResponse { // **INIT** happens once when the domain is loaded into Paladin, after the result of ConfigureDomain has been processed message InitDomainRequest { - string domain_uuid = 1; // A runtime instance UUID of this domain, that it must supply on all requests - repeated StateSchema abi_state_schemas = 2; // The deterministically hashed identifiers of the same array of schemas provied to Paladin in the DomainConfig + repeated StateSchema abi_state_schemas = 1; // The deterministically hashed identifiers of the same array of schemas provied to Paladin in the DomainConfig } message InitDomainResponse { From e3f3797b5bfada7913c1b36e5ed2e41c5284b2a0 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 15:42:58 -0400 Subject: [PATCH 15/26] ContractAddress should be tktypes.EthAddress Signed-off-by: Andrew Richardson --- core/go/internal/domainmgr/domain.go | 6 +- core/go/internal/domainmgr/domain_test.go | 23 ++++++-- .../domainmgr/private_smart_contract.go | 6 +- .../domainmgr/private_smart_contract_test.go | 8 +-- core/go/internal/msgs/en_errors.go | 1 + core/go/internal/statestore/abi_schema.go | 17 +++--- .../go/internal/statestore/abi_schema_test.go | 24 ++++---- core/go/internal/statestore/domain_context.go | 13 ++--- .../statestore/domain_context_test.go | 57 ++++++++++++------- core/go/internal/statestore/schema.go | 2 +- core/go/internal/statestore/state.go | 22 +++---- .../internal/statestore/state_status_test.go | 19 ++++--- core/go/internal/statestore/state_test.go | 30 ++++++---- core/go/internal/statestore/state_writer.go | 4 +- core/go/internal/statestore/statestore.go | 5 +- core/go/internal/statestore/statestore_rpc.go | 4 +- .../statestore/statestore_rpc_test.go | 5 +- toolkit/go/pkg/tktypes/eth_address.go | 4 ++ toolkit/go/pkg/tktypes/eth_address_test.go | 3 + 19 files changed, 153 insertions(+), 100 deletions(-) diff --git a/core/go/internal/domainmgr/domain.go b/core/go/internal/domainmgr/domain.go index 1be532264..8f2dbc74a 100644 --- a/core/go/internal/domainmgr/domain.go +++ b/core/go/internal/domainmgr/domain.go @@ -192,9 +192,13 @@ func (d *domain) FindAvailableStates(ctx context.Context, req *prototk.FindAvail if err != nil { return nil, i18n.WrapError(ctx, err, msgs.MsgDomainInvalidQueryJSON) } + addr, err := tktypes.ParseEthAddress(req.ContractAddress) + if err != nil { + return nil, i18n.WrapError(ctx, err, msgs.MsgDomainErrorParsingAddress) + } var states []*statestore.State - err = d.dm.stateStore.RunInDomainContext(d.name, req.ContractAddress, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { + err = d.dm.stateStore.RunInDomainContext(d.name, *addr, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { states, err = dsi.FindAvailableStates(req.SchemaId, &query) return err }) diff --git a/core/go/internal/domainmgr/domain_test.go b/core/go/internal/domainmgr/domain_test.go index e2a9d9994..6b716efd0 100644 --- a/core/go/internal/domainmgr/domain_test.go +++ b/core/go/internal/domainmgr/domain_test.go @@ -315,6 +315,18 @@ func TestDomainFindAvailableStatesBadQuery(t *testing.T) { assert.Regexp(t, "PD011608", err) } +func TestDomainFindAvailableStatesBadAddress(t *testing.T) { + ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), mockSchemas()) + defer done() + assert.Nil(t, tp.d.initError.Load()) + _, err := tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ + SchemaId: "12345", + ContractAddress: "0x", + QueryJson: `{}`, + }) + assert.Regexp(t, "PD011641", err) +} + func TestDomainFindAvailableStatesFail(t *testing.T) { ctx, _, tp, done := newTestDomain(t, false, goodDomainConf(), func(mc *mockComponents) { mc.stateStore.On("EnsureABISchemas", mock.Anything, "test1", mock.Anything).Return([]statestore.Schema{}, nil) @@ -323,14 +335,14 @@ func TestDomainFindAvailableStatesFail(t *testing.T) { defer done() assert.Nil(t, tp.d.initError.Load()) _, err := tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - ContractAddress: "0x1234", + ContractAddress: tktypes.RandAddress().String(), SchemaId: "12345", QueryJson: `{}`, }) assert.Regexp(t, "pop", err) } -func storeState(t *testing.T, dm *domainManager, tp *testPlugin, contractAddress string, txID uuid.UUID, amount *ethtypes.HexInteger) *fakeState { +func storeState(t *testing.T, dm *domainManager, tp *testPlugin, contractAddress tktypes.EthAddress, txID uuid.UUID, amount *ethtypes.HexInteger) *fakeState { state := &fakeState{ Salt: tktypes.Bytes32(tktypes.RandBytes(32)), Owner: tktypes.EthAddress(tktypes.RandBytes(20)), @@ -360,11 +372,12 @@ func TestDomainFindAvailableStatesOK(t *testing.T) { assert.Nil(t, tp.d.initError.Load()) txID := uuid.New() - state1 := storeState(t, dm, tp, "0x1234", txID, ethtypes.NewHexIntegerU64(100000000)) + contractAddress := tktypes.RandAddress() + state1 := storeState(t, dm, tp, *contractAddress, txID, ethtypes.NewHexIntegerU64(100000000)) // Filter match states, err := tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - ContractAddress: "0x1234", + ContractAddress: contractAddress.String(), SchemaId: tp.stateSchemas[0].Id, QueryJson: `{ "eq": [ @@ -377,7 +390,7 @@ func TestDomainFindAvailableStatesOK(t *testing.T) { // Filter miss states, err = tp.d.FindAvailableStates(ctx, &prototk.FindAvailableStatesRequest{ - ContractAddress: "0x1234", + ContractAddress: contractAddress.String(), SchemaId: tp.stateSchemas[0].Id, QueryJson: `{ "eq": [ diff --git a/core/go/internal/domainmgr/private_smart_contract.go b/core/go/internal/domainmgr/private_smart_contract.go index bf1cde9d6..89a16e078 100644 --- a/core/go/internal/domainmgr/private_smart_contract.go +++ b/core/go/internal/domainmgr/private_smart_contract.go @@ -189,7 +189,7 @@ func (dc *domainContract) WritePotentialStates(ctx context.Context, tx *componen var states []*statestore.State if len(newStatesToWrite) > 0 { - err := dc.dm.stateStore.RunInDomainContext(domain.name, dc.info.Address.String(), func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { + err := dc.dm.stateStore.RunInDomainContext(domain.name, dc.info.Address, func(ctx context.Context, dsi statestore.DomainStateInterface) (err error) { states, err = dsi.UpsertStates(&tx.ID, newStatesToWrite) return err }) @@ -254,7 +254,7 @@ func (dc *domainContract) LockStates(ctx context.Context, tx *components.Private }) } - return dc.dm.stateStore.RunInDomainContext(dc.d.name, dc.info.Address.String(), func(ctx context.Context, dsi statestore.DomainStateInterface) error { + return dc.dm.stateStore.RunInDomainContext(dc.d.name, dc.info.Address, func(ctx context.Context, dsi statestore.DomainStateInterface) error { // Heavy lifting is all done for us by the state store _, err := dsi.UpsertStates(&tx.ID, txLockedStateUpserts) if err == nil && len(readStateUpserts) > 0 { @@ -416,7 +416,7 @@ func (dc *domainContract) loadStates(ctx context.Context, refs []*prototk.StateR stateIDs[i] = stateID } statesByID := make(map[tktypes.Bytes32]*statestore.State) - err := dc.dm.stateStore.RunInDomainContext(dc.d.name, dc.info.Address.String(), func(ctx context.Context, dsi statestore.DomainStateInterface) error { + err := dc.dm.stateStore.RunInDomainContext(dc.d.name, dc.info.Address, func(ctx context.Context, dsi statestore.DomainStateInterface) error { for schemaID, stateIDs := range rawIDsBySchema { statesForSchema, err := dsi.FindAvailableStates(schemaID, &query.QueryJSON{ Statements: query.Statements{ diff --git a/core/go/internal/domainmgr/private_smart_contract_test.go b/core/go/internal/domainmgr/private_smart_contract_test.go index 39780fb03..c87b3109b 100644 --- a/core/go/internal/domainmgr/private_smart_contract_test.go +++ b/core/go/internal/domainmgr/private_smart_contract_test.go @@ -310,10 +310,10 @@ func TestFullTransactionRealDBOK(t *testing.T) { psc, tx := doDomainInitTransactionOK(t, ctx, tp) domain := tp.d - state1 := storeState(t, dm, tp, psc.Address().String(), tx.ID, ethtypes.NewHexInteger64(1111111)) - state2 := storeState(t, dm, tp, psc.Address().String(), tx.ID, ethtypes.NewHexInteger64(2222222)) - state3 := storeState(t, dm, tp, psc.Address().String(), tx.ID, ethtypes.NewHexInteger64(3333333)) - state4 := storeState(t, dm, tp, psc.Address().String(), tx.ID, ethtypes.NewHexInteger64(4444444)) + state1 := storeState(t, dm, tp, psc.Address(), tx.ID, ethtypes.NewHexInteger64(1111111)) + state2 := storeState(t, dm, tp, psc.Address(), tx.ID, ethtypes.NewHexInteger64(2222222)) + state3 := storeState(t, dm, tp, psc.Address(), tx.ID, ethtypes.NewHexInteger64(3333333)) + state4 := storeState(t, dm, tp, psc.Address(), tx.ID, ethtypes.NewHexInteger64(4444444)) state5 := &fakeState{ Salt: tktypes.Bytes32(tktypes.RandBytes(32)), diff --git a/core/go/internal/msgs/en_errors.go b/core/go/internal/msgs/en_errors.go index f4e594e00..ea300a219 100644 --- a/core/go/internal/msgs/en_errors.go +++ b/core/go/internal/msgs/en_errors.go @@ -246,6 +246,7 @@ var ( MsgDomainABIRecoverRequestSignature = ffe("PD011638", "Invalid signature") MsgDomainABIEncodingTypedDataInvalid = ffe("PD011639", "EIP-712 typed data V4 encoding request invalid") MsgDomainABIEncodingTypedDataFail = ffe("PD011640", "EIP-712 typed data V4 encoding request failed") + MsgDomainErrorParsingAddress = ffe("PD011641", "Error parsing address") // Entrypoint PD0117XX MsgEntrypointUnknownEngine = ffe("PD011700", "Unknown engine '%s'") diff --git a/core/go/internal/statestore/abi_schema.go b/core/go/internal/statestore/abi_schema.go index 74dca65bc..3406ad96f 100644 --- a/core/go/internal/statestore/abi_schema.go +++ b/core/go/internal/statestore/abi_schema.go @@ -323,7 +323,7 @@ func (as *abiSchema) parseStateData(ctx context.Context, data tktypes.RawJSON) ( // Take the state, parse the value into the type tree of this schema, and from that // build the label values to store in the DB for comparison appropriate to the type. -func (as *abiSchema) ProcessState(ctx context.Context, data tktypes.RawJSON) (*StateWithLabels, error) { +func (as *abiSchema) ProcessState(ctx context.Context, contractAddress tktypes.EthAddress, data tktypes.RawJSON) (*StateWithLabels, error) { psd, err := as.parseStateData(ctx, data) if err != nil { @@ -355,13 +355,14 @@ func (as *abiSchema) ProcessState(ctx context.Context, data tktypes.RawJSON) (*S now := tktypes.TimestampNow() return &StateWithLabels{ State: &State{ - ID: hashID, - CreatedAt: now, - DomainName: as.DomainName, - Schema: as.ID, - Data: jsonData, - Labels: psd.labels, - Int64Labels: psd.int64Labels, + ID: hashID, + CreatedAt: now, + DomainName: as.DomainName, + Schema: as.ID, + ContractAddress: contractAddress, + Data: jsonData, + Labels: psd.labels, + Int64Labels: psd.int64Labels, }, LabelValues: addStateBaseLabels(psd.labelValues, hashID, now), }, nil diff --git a/core/go/internal/statestore/abi_schema_test.go b/core/go/internal/statestore/abi_schema_test.go index 54c1ca1df..e9144732b 100644 --- a/core/go/internal/statestore/abi_schema_test.go +++ b/core/go/internal/statestore/abi_schema_test.go @@ -104,9 +104,10 @@ func TestStoreRetrieveABISchema(t *testing.T) { err = ss.persistSchemas([]*SchemaPersisted{as.SchemaPersisted}) require.NoError(t, err) schemaID := as.Persisted().ID.String() + contractAddress := tktypes.RandAddress() // Check it handles data - state1, err := ss.PersistState(ctx, "domain1", "0x1234", schemaID, tktypes.RawJSON(`{ + state1, err := ss.PersistState(ctx, "domain1", *contractAddress, schemaID, tktypes.RawJSON(`{ "field1": "0x0123456789012345678901234567890123456789", "field2": "hello world", "field3": 42, @@ -119,7 +120,6 @@ func TestStoreRetrieveABISchema(t *testing.T) { "cruft": "to remove" }`)) require.NoError(t, err) - require.NoError(t, err) assert.Equal(t, []*StateLabel{ // uint256 written as zero padded string {State: state1.ID, Label: "field1", Value: "0000000000000000000000000123456789012345678901234567890123456789"}, @@ -181,7 +181,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { getValidate() // Get the state back too - state1a, err := ss.GetState(ctx, as.Persisted().DomainName, state1.ID.String(), true, true) + state1a, err := ss.GetState(ctx, as.Persisted().DomainName, *contractAddress, state1.ID.String(), true, true) require.NoError(t, err) assert.Equal(t, state1.State, state1a) @@ -200,7 +200,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err := ss.FindStates(ctx, as.Persisted().DomainName, "0x1234", schemaID, query, "all") + states, err := ss.FindStates(ctx, as.Persisted().DomainName, *contractAddress, schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 1) @@ -211,7 +211,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err = ss.FindStates(ctx, as.Persisted().DomainName, "0x1234", schemaID, query, "all") + states, err = ss.FindStates(ctx, as.Persisted().DomainName, *contractAddress, schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 0) @@ -222,7 +222,7 @@ func TestStoreRetrieveABISchema(t *testing.T) { ] }`), &query) require.NoError(t, err) - states, err = ss.FindStates(ctx, as.Persisted().DomainName, "0x1234", schemaID, query, "all") + states, err = ss.FindStates(ctx, as.Persisted().DomainName, *contractAddress, schemaID, query, "all") require.NoError(t, err) assert.Len(t, states, 0) } @@ -430,7 +430,7 @@ func TestABISchemaProcessStateInvalidType(t *testing.T) { var err error as.tc, err = as.definition.TypeComponentTreeCtx(ctx) require.NoError(t, err) - _, err = as.ProcessState(ctx, tktypes.RawJSON(`{"field1": 12345}`)) + _, err = as.ProcessState(ctx, *tktypes.RandAddress(), tktypes.RawJSON(`{"field1": 12345}`)) assert.Regexp(t, "PD010103", err) } @@ -462,7 +462,7 @@ func TestABISchemaProcessStateLabelMissing(t *testing.T) { var err error as.tc, err = as.definition.TypeComponentTreeCtx(ctx) require.NoError(t, err) - _, err = as.ProcessState(ctx, tktypes.RawJSON(`{"field1": 12345}`)) + _, err = as.ProcessState(ctx, *tktypes.RandAddress(), tktypes.RawJSON(`{"field1": 12345}`)) assert.Regexp(t, "PD010110", err) } @@ -498,7 +498,7 @@ func TestABISchemaProcessStateBadValue(t *testing.T) { var err error as.tc, err = as.definition.TypeComponentTreeCtx(ctx) require.NoError(t, err) - _, err = as.ProcessState(ctx, tktypes.RawJSON(`{!!! wrong`)) + _, err = as.ProcessState(ctx, *tktypes.RandAddress(), tktypes.RawJSON(`{!!! wrong`)) assert.Regexp(t, "PD010116", err) } @@ -523,7 +523,7 @@ func TestABISchemaProcessStateMismatchValue(t *testing.T) { var err error as.tc, err = as.definition.TypeComponentTreeCtx(ctx) require.NoError(t, err) - _, err = as.ProcessState(ctx, tktypes.RawJSON(`{"field1":{}}`)) + _, err = as.ProcessState(ctx, *tktypes.RandAddress(), tktypes.RawJSON(`{"field1":{}}`)) assert.Regexp(t, "FF22030", err) } @@ -548,7 +548,7 @@ func TestABISchemaProcessStateEIP712Failure(t *testing.T) { var err error as.tc, err = as.definition.TypeComponentTreeCtx(ctx) require.NoError(t, err) - _, err = as.ProcessState(ctx, tktypes.RawJSON(`{"field1":"0x753A7decf94E48a05Fa1B342D8984acA9bFaf6B2"}`)) + _, err = as.ProcessState(ctx, *tktypes.RandAddress(), tktypes.RawJSON(`{"field1":"0x753A7decf94E48a05Fa1B342D8984acA9bFaf6B2"}`)) assert.Regexp(t, "FF22073", err) } @@ -573,7 +573,7 @@ func TestABISchemaProcessStateDataFailure(t *testing.T) { var err error as.tc, err = as.definition.TypeComponentTreeCtx(ctx) require.NoError(t, err) - _, err = as.ProcessState(ctx, tktypes.RawJSON(`{"field1":"0x753A7decf94E48a05Fa1B342D8984acA9bFaf6B2"}`)) + _, err = as.ProcessState(ctx, *tktypes.RandAddress(), tktypes.RawJSON(`{"field1":"0x753A7decf94E48a05Fa1B342D8984acA9bFaf6B2"}`)) assert.Regexp(t, "FF22073", err) } diff --git a/core/go/internal/statestore/domain_context.go b/core/go/internal/statestore/domain_context.go index 0c6619638..9a7a6b170 100644 --- a/core/go/internal/statestore/domain_context.go +++ b/core/go/internal/statestore/domain_context.go @@ -99,7 +99,7 @@ type DomainStateInterface interface { type domainContext struct { ss *stateStore domainName string - contractAddress string + contractAddress tktypes.EthAddress ctx context.Context stateLock sync.Mutex latch chan struct{} @@ -108,11 +108,11 @@ type domainContext struct { flushResult chan error } -func (ss *stateStore) RunInDomainContext(domainName, contractAddress string, fn DomainContextFunction) error { +func (ss *stateStore) RunInDomainContext(domainName string, contractAddress tktypes.EthAddress, fn DomainContextFunction) error { return ss.getDomainContext(domainName, contractAddress).run(fn) } -func (ss *stateStore) RunInDomainContextFlush(domainName, contractAddress string, fn DomainContextFunction) error { +func (ss *stateStore) RunInDomainContextFlush(domainName string, contractAddress tktypes.EthAddress, fn DomainContextFunction) error { dc := ss.getDomainContext(domainName, contractAddress) err := dc.run(fn) if err == nil { @@ -124,11 +124,11 @@ func (ss *stateStore) RunInDomainContextFlush(domainName, contractAddress string return err } -func (ss *stateStore) getDomainContext(domainName, contractAddress string) *domainContext { +func (ss *stateStore) getDomainContext(domainName string, contractAddress tktypes.EthAddress) *domainContext { ss.domainLock.Lock() defer ss.domainLock.Unlock() - domainKey := domainName + ":" + contractAddress + domainKey := domainName + ":" + contractAddress.String() dc := ss.domainContexts[domainKey] if dc == nil { dc = &domainContext{ @@ -322,12 +322,11 @@ func (dc *domainContext) UpsertStates(transactionID *uuid.UUID, stateUpserts []* return nil, err } - withValues[i], err = schema.ProcessState(dc.ctx, ns.Data) + withValues[i], err = schema.ProcessState(dc.ctx, dc.contractAddress, ns.Data) if err != nil { return nil, err } states[i] = withValues[i].State - states[i].ContractAddress = dc.contractAddress if transactionID != nil { states[i].Locked = &StateLock{ Transaction: *transactionID, diff --git a/core/go/internal/statestore/domain_context_test.go b/core/go/internal/statestore/domain_context_test.go index 3f481934f..94076a498 100644 --- a/core/go/internal/statestore/domain_context_test.go +++ b/core/go/internal/statestore/domain_context_test.go @@ -71,8 +71,9 @@ func TestStateFlushAsync(t *testing.T) { _, ss, done := newDBTestStateStore(t) defer done() + contractAddress := tktypes.RandAddress() flushed := make(chan bool) - err := ss.RunInDomainContext("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + err := ss.RunInDomainContext("domain1", *contractAddress, func(ctx context.Context, dsi DomainStateInterface) error { return dsi.Flush(func(ctx context.Context, dsi DomainStateInterface) error { flushed <- true return nil @@ -109,7 +110,8 @@ func TestUpsertSchemaAndStates(t *testing.T) { require.Len(t, schemas, 1) schemaID := schemas[0].IDString() - err = ss.RunInDomainContext("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + contractAddress := tktypes.RandAddress() + err = ss.RunInDomainContext("domain1", *contractAddress, func(ctx context.Context, dsi DomainStateInterface) error { states, err := dsi.UpsertStates(nil, []*StateUpsert{ { SchemaID: schemaID, @@ -138,7 +140,8 @@ func TestStateContextMintSpendMint(t *testing.T) { assert.Len(t, schemas, 1) schemaID = schemas[0].IDString() - err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + contractAddress := tktypes.RandAddress() + err = ss.RunInDomainContextFlush("domain1", *contractAddress, func(ctx context.Context, dsi DomainStateInterface) error { // Store some states tx1states, err := dsi.UpsertStates(&transactionID, []*StateUpsert{ @@ -218,7 +221,7 @@ func TestStateContextMintSpendMint(t *testing.T) { }) require.NoError(t, err) - err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + err = ss.RunInDomainContextFlush("domain1", *contractAddress, func(ctx context.Context, dsi DomainStateInterface) error { // Check the DB persisted state is what we expect states, err := dsi.FindAvailableStates(schemaID, toQuery(t, `{ "sort": [ "owner", "amount" ] @@ -279,7 +282,7 @@ func TestStateContextMintSpendMint(t *testing.T) { }) require.NoError(t, err) - err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + err = ss.RunInDomainContextFlush("domain1", *contractAddress, func(ctx context.Context, dsi DomainStateInterface) error { // Confirm states, err := dsi.FindAvailableStates(schemaID, toQuery(t, `{}`)) @@ -296,7 +299,8 @@ func TestDSILatch(t *testing.T) { _, ss, done := newDBTestStateStore(t) - dsi := ss.getDomainContext("domain1", "0x1234") + contractAddress := tktypes.RandAddress() + dsi := ss.getDomainContext("domain1", *contractAddress) err := dsi.takeLatch() require.NoError(t, err) @@ -330,7 +334,8 @@ func TestDSIFlushErrorCapture(t *testing.T) { schemas, err := ss.EnsureABISchemas(context.Background(), "domain1", []*abi.Parameter{testABIParam(t, fakeCoinABI)}) require.NoError(t, err) - err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + contractAddress := tktypes.RandAddress() + err = ss.RunInDomainContextFlush("domain1", *contractAddress, func(ctx context.Context, dsi DomainStateInterface) error { dc := dsi.(*domainContext) @@ -379,9 +384,10 @@ func TestDSIMergedUnFlushedWhileFlushing(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1", "0x1234") + contractAddress := tktypes.RandAddress() + dc := ss.getDomainContext("domain1", *contractAddress) - s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( + s1, err := schema.ProcessState(ctx, *contractAddress, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32)))) require.NoError(t, err) @@ -415,9 +421,10 @@ func TestDSIMergedUnFlushedWhileFlushingDedup(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1", "0x1234") + contractAddress := tktypes.RandAddress() + dc := ss.getDomainContext("domain1", *contractAddress) - s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( + s1, err := schema.ProcessState(ctx, *contractAddress, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32)))) require.NoError(t, err) @@ -457,9 +464,10 @@ func TestDSIMergedUnFlushedEvalError(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1", "0x1234") + contractAddress := tktypes.RandAddress() + dc := ss.getDomainContext("domain1", *contractAddress) - s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( + s1, err := schema.ProcessState(ctx, *contractAddress, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32)))) require.NoError(t, err) @@ -483,9 +491,10 @@ func TestDSIMergedInMemoryMatchesRecoverLabelsFail(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1", "0x1234") + contractAddress := tktypes.RandAddress() + dc := ss.getDomainContext("domain1", *contractAddress) - s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( + s1, err := schema.ProcessState(ctx, *contractAddress, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32)))) require.NoError(t, err) @@ -510,9 +519,10 @@ func TestDSIMergedInMemoryMatchesSortFail(t *testing.T) { schema, err := newABISchema(ctx, "domain1", testABIParam(t, fakeCoinABI)) require.NoError(t, err) - dc := ss.getDomainContext("domain1", "0x1234") + contractAddress := tktypes.RandAddress() + dc := ss.getDomainContext("domain1", *contractAddress) - s1, err := schema.ProcessState(ctx, tktypes.RawJSON(fmt.Sprintf( + s1, err := schema.ProcessState(ctx, *contractAddress, tktypes.RawJSON(fmt.Sprintf( `{"amount": 20, "owner": "0x615dD09124271D8008225054d85Ffe720E7a447A", "salt": "%s"}`, tktypes.RandHex(32)))) require.NoError(t, err) @@ -539,7 +549,8 @@ func TestDSIFindBadQueryAndInsert(t *testing.T) { schemaID := schemas[0].IDString() assert.Equal(t, "type=FakeCoin(bytes32 salt,address owner,uint256 amount),labels=[owner,amount]", schemas[0].Signature()) - err = ss.RunInDomainContextFlush("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + contractAddress := tktypes.RandAddress() + err = ss.RunInDomainContextFlush("domain1", *contractAddress, func(ctx context.Context, dsi DomainStateInterface) error { _, err = dsi.FindAvailableStates(schemaID, toQuery(t, `{"sort":["wrong"]}`)) assert.Regexp(t, "PD010700", err) @@ -560,7 +571,8 @@ func TestDSIBadIDs(t *testing.T) { _, ss, _, done := newDBMockStateStore(t) defer done() - _ = ss.RunInDomainContext("domain1", "0x1234", func(ctx context.Context, dsi DomainStateInterface) error { + contractAddress := tktypes.RandAddress() + _ = ss.RunInDomainContext("domain1", *contractAddress, func(ctx context.Context, dsi DomainStateInterface) error { _, err := dsi.UpsertStates(nil, []*StateUpsert{ {SchemaID: "wrong"}, @@ -583,7 +595,8 @@ func TestDSIResetWithMixed(t *testing.T) { _, ss, _, done := newDBMockStateStore(t) defer done() - dc := ss.getDomainContext("domain1", "0x1234") + contractAddress := tktypes.RandAddress() + dc := ss.getDomainContext("domain1", *contractAddress) state1 := tktypes.Bytes32Keccak(([]byte)("state1")) transactionID1 := uuid.New() @@ -606,7 +619,9 @@ func TestDSIResetWithMixed(t *testing.T) { func TestCheckEvalGTTimestamp(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - dc := ss.getDomainContext("domain1", "0x1234") + + contractAddress := tktypes.RandAddress() + dc := ss.getDomainContext("domain1", *contractAddress) filterJSON := `{"gt":[{"field":".created","value":1726545933211347000}],"limit":10,"sort":[".created"]}` diff --git a/core/go/internal/statestore/schema.go b/core/go/internal/statestore/schema.go index 059be10c5..bc1136ae9 100644 --- a/core/go/internal/statestore/schema.go +++ b/core/go/internal/statestore/schema.go @@ -72,7 +72,7 @@ type Schema interface { IDString() string Signature() string Persisted() *SchemaPersisted - ProcessState(ctx context.Context, data tktypes.RawJSON) (*StateWithLabels, error) + ProcessState(ctx context.Context, contractAddress tktypes.EthAddress, data tktypes.RawJSON) (*StateWithLabels, error) RecoverLabels(ctx context.Context, s *State) (*StateWithLabels, error) } diff --git a/core/go/internal/statestore/state.go b/core/go/internal/statestore/state.go index 5729044ad..1d8449b62 100644 --- a/core/go/internal/statestore/state.go +++ b/core/go/internal/statestore/state.go @@ -34,7 +34,7 @@ type State struct { CreatedAt tktypes.Timestamp `json:"created" gorm:"autoCreateTime:nano"` DomainName string `json:"domain"` Schema tktypes.Bytes32 `json:"schema"` - ContractAddress string `json:"contractAddress"` + ContractAddress tktypes.EthAddress `json:"contractAddress"` Data tktypes.RawJSON `json:"data"` Labels []*StateLabel `json:"-" gorm:"foreignKey:state;references:id;"` Int64Labels []*StateInt64Label `json:"-" gorm:"foreignKey:state;references:id;"` @@ -77,18 +77,17 @@ func (s *StateWithLabels) ValueSet() filters.ValueSet { return s.LabelValues } -func (ss *stateStore) PersistState(ctx context.Context, domainName, contractAddress, schemaID string, data tktypes.RawJSON) (*StateWithLabels, error) { +func (ss *stateStore) PersistState(ctx context.Context, domainName string, contractAddress tktypes.EthAddress, schemaID string, data tktypes.RawJSON) (*StateWithLabels, error) { schema, err := ss.GetSchema(ctx, domainName, schemaID, true) if err != nil { return nil, err } - s, err := schema.ProcessState(ctx, data) + s, err := schema.ProcessState(ctx, contractAddress, data) if err != nil { return nil, err } - s.ContractAddress = contractAddress op := ss.writer.newWriteOp(s.State.DomainName, contractAddress) op.states = []*StateWithLabels{s} @@ -96,7 +95,7 @@ func (ss *stateStore) PersistState(ctx context.Context, domainName, contractAddr return s, op.flush(ctx) } -func (ss *stateStore) GetState(ctx context.Context, domainName, stateID string, failNotFound, withLabels bool) (*State, error) { +func (ss *stateStore) GetState(ctx context.Context, domainName string, contractAddress tktypes.EthAddress, stateID string, failNotFound, withLabels bool) (*State, error) { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return nil, err @@ -109,6 +108,7 @@ func (ss *stateStore) GetState(ctx context.Context, domainName, stateID string, var states []*State err = q. Where("domain_name = ?", domainName). + Where("contract_address = ?", contractAddress). Where("id = ?", id). Limit(1). Find(&states). @@ -158,12 +158,12 @@ func (ss *stateStore) labelSetFor(schema Schema) *trackingLabelSet { return &tls } -func (ss *stateStore) FindStates(ctx context.Context, domainName, contractAddress, schemaID string, query *query.QueryJSON, status StateStatusQualifier) (s []*State, err error) { +func (ss *stateStore) FindStates(ctx context.Context, domainName string, contractAddress tktypes.EthAddress, schemaID string, query *query.QueryJSON, status StateStatusQualifier) (s []*State, err error) { _, s, err = ss.findStates(ctx, domainName, contractAddress, schemaID, query, status) return s, err } -func (ss *stateStore) findStates(ctx context.Context, domainName, contractAddress, schemaID string, jq *query.QueryJSON, status StateStatusQualifier, excluded ...*idOnly) (schema Schema, s []*State, err error) { +func (ss *stateStore) findStates(ctx context.Context, domainName string, contractAddress tktypes.EthAddress, schemaID string, jq *query.QueryJSON, status StateStatusQualifier, excluded ...*idOnly) (schema Schema, s []*State, err error) { if len(jq.Sort) == 0 { jq.Sort = []string{".created"} } @@ -213,7 +213,7 @@ func (ss *stateStore) findStates(ctx context.Context, domainName, contractAddres return schema, states, nil } -func (ss *stateStore) MarkConfirmed(ctx context.Context, domainName, contractAddress, stateID string, transactionID uuid.UUID) error { +func (ss *stateStore) MarkConfirmed(ctx context.Context, domainName string, contractAddress tktypes.EthAddress, stateID string, transactionID uuid.UUID) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err @@ -228,7 +228,7 @@ func (ss *stateStore) MarkConfirmed(ctx context.Context, domainName, contractAdd return op.flush(ctx) } -func (ss *stateStore) MarkSpent(ctx context.Context, domainName, contractAddress, stateID string, transactionID uuid.UUID) error { +func (ss *stateStore) MarkSpent(ctx context.Context, domainName string, contractAddress tktypes.EthAddress, stateID string, transactionID uuid.UUID) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err @@ -243,7 +243,7 @@ func (ss *stateStore) MarkSpent(ctx context.Context, domainName, contractAddress return op.flush(ctx) } -func (ss *stateStore) MarkLocked(ctx context.Context, domainName, contractAddress, stateID string, transactionID uuid.UUID, creating, spending bool) error { +func (ss *stateStore) MarkLocked(ctx context.Context, domainName string, contractAddress tktypes.EthAddress, stateID string, transactionID uuid.UUID, creating, spending bool) error { id, err := tktypes.ParseBytes32Ctx(ctx, stateID) if err != nil { return err @@ -258,7 +258,7 @@ func (ss *stateStore) MarkLocked(ctx context.Context, domainName, contractAddres return op.flush(ctx) } -func (ss *stateStore) ResetTransaction(ctx context.Context, domainName, contractAddress string, transactionID uuid.UUID) error { +func (ss *stateStore) ResetTransaction(ctx context.Context, domainName string, contractAddress tktypes.EthAddress, transactionID uuid.UUID) error { op := ss.writer.newWriteOp(domainName, contractAddress) op.transactionLockDeletes = []uuid.UUID{transactionID} diff --git a/core/go/internal/statestore/state_status_test.go b/core/go/internal/statestore/state_status_test.go index 6eaf82dee..4fb312d9b 100644 --- a/core/go/internal/statestore/state_status_test.go +++ b/core/go/internal/statestore/state_status_test.go @@ -54,7 +54,7 @@ const widgetABI = `{ ] }` -func makeWidgets(t *testing.T, ctx context.Context, ss *stateStore, domainName, schemaID string, withoutSalt []string) []*StateWithLabels { +func makeWidgets(t *testing.T, ctx context.Context, ss *stateStore, domainName string, contractAddress tktypes.EthAddress, schemaID string, withoutSalt []string) []*StateWithLabels { states := make([]*StateWithLabels, len(withoutSalt)) for i, w := range withoutSalt { var ij map[string]interface{} @@ -63,7 +63,7 @@ func makeWidgets(t *testing.T, ctx context.Context, ss *stateStore, domainName, ij["salt"] = tktypes.RandHex(32) withSalt, err := json.Marshal(ij) require.NoError(t, err) - states[i], err = ss.PersistState(ctx, domainName, "0x1234", schemaID, withSalt) + states[i], err = ss.PersistState(ctx, domainName, contractAddress, schemaID, withSalt) require.NoError(t, err) fmt.Printf("widget[%d]: %s\n", i, states[i].Data) } @@ -88,7 +88,8 @@ func TestStateLockingQuery(t *testing.T) { require.NoError(t, err) schemaID := schema.IDString() - widgets := makeWidgets(t, ctx, ss, "domain1", schemaID, []string{ + contractAddress := tktypes.RandAddress() + widgets := makeWidgets(t, ctx, ss, "domain1", *contractAddress, schemaID, []string{ `{"size": 11111, "color": "red", "price": 100}`, `{"size": 22222, "color": "red", "price": 150}`, `{"size": 33333, "color": "blue", "price": 199}`, @@ -97,7 +98,7 @@ func TestStateLockingQuery(t *testing.T) { }) checkQuery := func(query string, status StateStatusQualifier, expected ...int) { - states, err := ss.FindStates(ctx, "domain1", "0x1234", schemaID, toQuery(t, query), status) + states, err := ss.FindStates(ctx, "domain1", *contractAddress, schemaID, toQuery(t, query), status) require.NoError(t, err) assert.Len(t, states, len(expected)) for _, wIndex := range expected { @@ -127,7 +128,7 @@ func TestStateLockingQuery(t *testing.T) { // Mark them all confirmed apart from one for i, w := range widgets { if i != 3 { - err = ss.MarkConfirmed(ctx, "domain1", "0x1234", w.ID.String(), uuid.New()) + err = ss.MarkConfirmed(ctx, "domain1", *contractAddress, w.ID.String(), uuid.New()) require.NoError(t, err) } } @@ -141,7 +142,7 @@ func TestStateLockingQuery(t *testing.T) { checkQuery(`{}`, seqQual) // unchanged // Mark one spent - err = ss.MarkSpent(ctx, "domain1", "0x1234", widgets[0].ID.String(), uuid.New()) + err = ss.MarkSpent(ctx, "domain1", *contractAddress, widgets[0].ID.String(), uuid.New()) require.NoError(t, err) checkQuery(`{}`, StateStatusAll, 0, 1, 2, 3, 4) // unchanged @@ -153,7 +154,7 @@ func TestStateLockingQuery(t *testing.T) { checkQuery(`{}`, seqQual) // unchanged // lock a confirmed one for spending - err = ss.MarkLocked(ctx, "domain1", "0x1234", widgets[1].ID.String(), seqID, false, true) + err = ss.MarkLocked(ctx, "domain1", *contractAddress, widgets[1].ID.String(), seqID, false, true) require.NoError(t, err) checkQuery(`{}`, StateStatusAll, 0, 1, 2, 3, 4) // unchanged @@ -165,7 +166,7 @@ func TestStateLockingQuery(t *testing.T) { checkQuery(`{}`, seqQual, 1) // added 1 // lock the unconfirmed one for spending - err = ss.MarkLocked(ctx, "domain1", "0x1234", widgets[3].ID.String(), seqID, false, true) + err = ss.MarkLocked(ctx, "domain1", *contractAddress, widgets[3].ID.String(), seqID, false, true) require.NoError(t, err) checkQuery(`{}`, StateStatusAll, 0, 1, 2, 3, 4) // unchanged @@ -181,7 +182,7 @@ func TestStateLockingQuery(t *testing.T) { checkQuery(`{"eq":[{"field":"color","value":"pink"}]}`, StateStatusAvailable) // clear the transaction locks - err = ss.ResetTransaction(ctx, "domain1", "0x1234", seqID) + err = ss.ResetTransaction(ctx, "domain1", *contractAddress, seqID) require.NoError(t, err) checkQuery(`{}`, StateStatusAll, 0, 1, 2, 3, 4) // unchanged diff --git a/core/go/internal/statestore/state_test.go b/core/go/internal/statestore/state_test.go index 64e7dc1f6..5425fa5f3 100644 --- a/core/go/internal/statestore/state_test.go +++ b/core/go/internal/statestore/state_test.go @@ -34,7 +34,8 @@ func TestPersistStateMissingSchema(t *testing.T) { db.ExpectQuery("SELECT").WillReturnRows(db.NewRows([]string{})) - _, err := ss.PersistState(ctx, "domain1", "0x1234", tktypes.Bytes32Keccak(([]byte)("test")).String(), nil) + contractAddress := tktypes.RandAddress() + _, err := ss.PersistState(ctx, "domain1", *contractAddress, tktypes.Bytes32Keccak(([]byte)("test")).String(), nil) assert.Regexp(t, "PD010106", err) } @@ -48,7 +49,8 @@ func TestPersistStateInvalidState(t *testing.T) { definition: &abi.Parameter{}, }) - _, err := ss.PersistState(ctx, "domain1", "0x1234", schemaID.String(), nil) + contractAddress := tktypes.RandAddress() + _, err := ss.PersistState(ctx, "domain1", *contractAddress, schemaID.String(), nil) assert.Regexp(t, "PD010116", err) } @@ -58,7 +60,8 @@ func TestGetStateMissing(t *testing.T) { db.ExpectQuery("SELECT").WillReturnRows(db.NewRows([]string{})) - _, err := ss.GetState(ctx, "domain1", tktypes.Bytes32Keccak(([]byte)("state1")).String(), true, false) + contractAddress := tktypes.RandAddress() + _, err := ss.GetState(ctx, "domain1", *contractAddress, tktypes.Bytes32Keccak(([]byte)("state1")).String(), true, false) assert.Regexp(t, "PD010112", err) } @@ -66,7 +69,8 @@ func TestGetStateBadID(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - _, err := ss.GetState(ctx, "domain1", "bad id", true, false) + contractAddress := tktypes.RandAddress() + _, err := ss.GetState(ctx, "domain1", *contractAddress, "bad id", true, false) assert.Regexp(t, "PD020007", err) } @@ -74,7 +78,8 @@ func TestMarkConfirmedBadID(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - err := ss.MarkConfirmed(ctx, "domain1", "0x1234", "bad id", uuid.New()) + contractAddress := tktypes.RandAddress() + err := ss.MarkConfirmed(ctx, "domain1", *contractAddress, "bad id", uuid.New()) assert.Regexp(t, "PD020007", err) } @@ -82,7 +87,8 @@ func TestMarkSpentBadID(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - err := ss.MarkSpent(ctx, "domain1", "0x1234", "bad id", uuid.New()) + contractAddress := tktypes.RandAddress() + err := ss.MarkSpent(ctx, "domain1", *contractAddress, "bad id", uuid.New()) assert.Regexp(t, "PD020007", err) } @@ -90,7 +96,8 @@ func TestMarkLockedBadID(t *testing.T) { ctx, ss, _, done := newDBMockStateStore(t) defer done() - err := ss.MarkLocked(ctx, "domain1", "0x1234", "bad id", uuid.New(), false, false) + contractAddress := tktypes.RandAddress() + err := ss.MarkLocked(ctx, "domain1", *contractAddress, "bad id", uuid.New(), false, false) assert.Regexp(t, "PD020007", err) } @@ -100,7 +107,8 @@ func TestFindStatesMissingSchema(t *testing.T) { db.ExpectQuery("SELECT").WillReturnRows(db.NewRows([]string{})) - _, err := ss.FindStates(ctx, "domain1", "0x1234", tktypes.Bytes32Keccak(([]byte)("schema1")).String(), &query.QueryJSON{}, "all") + contractAddress := tktypes.RandAddress() + _, err := ss.FindStates(ctx, "domain1", *contractAddress, tktypes.Bytes32Keccak(([]byte)("schema1")).String(), &query.QueryJSON{}, "all") assert.Regexp(t, "PD010106", err) } @@ -114,7 +122,8 @@ func TestFindStatesBadQuery(t *testing.T) { definition: &abi.Parameter{}, }) - _, err := ss.FindStates(ctx, "domain1", "0x1234", schemaID.String(), &query.QueryJSON{ + contractAddress := tktypes.RandAddress() + _, err := ss.FindStates(ctx, "domain1", *contractAddress, schemaID.String(), &query.QueryJSON{ Statements: query.Statements{ Ops: query.Ops{ Equal: []*query.OpSingleVal{ @@ -140,7 +149,8 @@ func TestFindStatesFail(t *testing.T) { db.ExpectQuery("SELECT.*created_at").WillReturnError(fmt.Errorf("pop")) - _, err := ss.FindStates(ctx, "domain1", "0x1234", schemaID.String(), &query.QueryJSON{ + contractAddress := tktypes.RandAddress() + _, err := ss.FindStates(ctx, "domain1", *contractAddress, schemaID.String(), &query.QueryJSON{ Statements: query.Statements{ Ops: query.Ops{ GreaterThan: []*query.OpSingleVal{ diff --git a/core/go/internal/statestore/state_writer.go b/core/go/internal/statestore/state_writer.go index 49340a6f2..3454a132f 100644 --- a/core/go/internal/statestore/state_writer.go +++ b/core/go/internal/statestore/state_writer.go @@ -86,10 +86,10 @@ func newStateWriter(bgCtx context.Context, ss *stateStore, conf *StateWriterConf return sw } -func (sw *stateWriter) newWriteOp(domainName, contractAddress string) *writeOperation { +func (sw *stateWriter) newWriteOp(domainName string, contractAddress tktypes.EthAddress) *writeOperation { return &writeOperation{ id: tktypes.ShortID(), - domainKey: domainName + ":" + contractAddress, + domainKey: domainName + ":" + contractAddress.String(), done: make(chan error, 1), // 1 slot to ensure we don't block the writer } } diff --git a/core/go/internal/statestore/statestore.go b/core/go/internal/statestore/statestore.go index f8576818b..b712a071a 100644 --- a/core/go/internal/statestore/statestore.go +++ b/core/go/internal/statestore/statestore.go @@ -25,6 +25,7 @@ import ( "github.com/kaleido-io/paladin/core/internal/rpcserver" "github.com/kaleido-io/paladin/core/pkg/persistence" "github.com/kaleido-io/paladin/toolkit/pkg/confutil" + "github.com/kaleido-io/paladin/toolkit/pkg/tktypes" ) type Config struct { @@ -46,8 +47,8 @@ var StateWriterConfigDefaults = StateWriterConfig{ type StateStore interface { RPCModule() *rpcserver.RPCModule - RunInDomainContext(domainName, contractAddress string, fn DomainContextFunction) error - RunInDomainContextFlush(domainName, contractAddress string, fn DomainContextFunction) error + RunInDomainContext(domainName string, contractAddress tktypes.EthAddress, fn DomainContextFunction) error + RunInDomainContextFlush(domainName string, contractAddress tktypes.EthAddress, fn DomainContextFunction) error EnsureABISchemas(ctx context.Context, domainName string, defs []*abi.Parameter) ([]Schema, error) Close() } diff --git a/core/go/internal/statestore/statestore_rpc.go b/core/go/internal/statestore/statestore_rpc.go index 9f11d433d..47db1673b 100644 --- a/core/go/internal/statestore/statestore_rpc.go +++ b/core/go/internal/statestore/statestore_rpc.go @@ -46,7 +46,7 @@ func (ss *stateStore) rpcListSchema() rpcserver.RPCHandler { func (ss *stateStore) rpcStoreState() rpcserver.RPCHandler { return rpcserver.RPCMethod4(func(ctx context.Context, domain string, - contractAddress string, + contractAddress tktypes.EthAddress, schema string, value tktypes.RawJSON, ) (*State, error) { @@ -62,7 +62,7 @@ func (ss *stateStore) rpcStoreState() rpcserver.RPCHandler { func (ss *stateStore) rpcQuery() rpcserver.RPCHandler { return rpcserver.RPCMethod5(func(ctx context.Context, domain string, - contractAddress string, + contractAddress tktypes.EthAddress, schema string, query query.QueryJSON, status StateStatusQualifier, diff --git a/core/go/internal/statestore/statestore_rpc_test.go b/core/go/internal/statestore/statestore_rpc_test.go index 4f4900f6a..94a48683c 100644 --- a/core/go/internal/statestore/statestore_rpc_test.go +++ b/core/go/internal/statestore/statestore_rpc_test.go @@ -81,8 +81,9 @@ func TestRPC(t *testing.T) { assert.Equal(t, SchemaTypeABI, schemas[0].Type) assert.Equal(t, "0x3612029bf239cbed1e27548e9211ecfe72496dfec4183fd3ea79a3a54eb126be", schemas[0].ID.String()) + contractAddress := tktypes.RandAddress() var state *State - rpcErr = c.CallRPC(ctx, &state, "pstate_storeState", "domain1", "0x1234", schemas[0].ID, tktypes.RawJSON(`{ + rpcErr = c.CallRPC(ctx, &state, "pstate_storeState", "domain1", contractAddress.String(), schemas[0].ID, tktypes.RawJSON(`{ "salt": "fd2724ce91a859e24c228e50ae17b9443454514edce9a64437c208b0184d8910", "size": 10, "color": "blue", @@ -95,7 +96,7 @@ func TestRPC(t *testing.T) { assert.Equal(t, "0x30e278bca8d876cdceb24520b0ebe736a64a9cb8019157f40fa5b03f083f824d", state.ID.String()) var states []*State - rpcErr = c.CallRPC(ctx, &states, "pstate_queryStates", "domain1", "0x1234", schemas[0].ID, tktypes.RawJSON(`{ + rpcErr = c.CallRPC(ctx, &states, "pstate_queryStates", "domain1", contractAddress.String(), schemas[0].ID, tktypes.RawJSON(`{ "eq": [{ "field": "color", "value": "blue" diff --git a/toolkit/go/pkg/tktypes/eth_address.go b/toolkit/go/pkg/tktypes/eth_address.go index 34dca0ff8..bd631badc 100644 --- a/toolkit/go/pkg/tktypes/eth_address.go +++ b/toolkit/go/pkg/tktypes/eth_address.go @@ -50,6 +50,10 @@ func EthAddressBytes(b []byte) *EthAddress { return &a } +func RandAddress() *EthAddress { + return (*EthAddress)(RandBytes(20)) +} + func (a *EthAddress) Address0xHex() *ethtypes.Address0xHex { return (*ethtypes.Address0xHex)(a) } diff --git a/toolkit/go/pkg/tktypes/eth_address_test.go b/toolkit/go/pkg/tktypes/eth_address_test.go index cf20ef702..462c41bf9 100644 --- a/toolkit/go/pkg/tktypes/eth_address_test.go +++ b/toolkit/go/pkg/tktypes/eth_address_test.go @@ -92,6 +92,9 @@ func TestEthAddress(t *testing.T) { a8 := &EthAddress{} err = a8.Scan("!!aca6d8ba6bff0fa5c8a06a58368cb6097285d5") assert.Regexp(t, "bad address", err) + + a9 := RandAddress() + assert.False(t, a9.IsZero()) } func TestEthAddressJSON(t *testing.T) { From 6d955321fd3298e8c0a6bafc54a8bba25bcb7d49 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Tue, 17 Sep 2024 15:53:40 -0400 Subject: [PATCH 16/26] Use address type for FindCoins on domain Signed-off-by: Andrew Richardson --- domains/noto/internal/noto/e2e_noto_test.go | 12 ++++++------ domains/noto/internal/noto/noto.go | 4 ++-- domains/noto/pkg/noto/noto.go | 3 ++- domains/zeto/integration-test/e2e_test.go | 4 ++-- domains/zeto/internal/zeto/zeto.go | 4 ++-- domains/zeto/pkg/zeto/zeto.go | 3 ++- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/domains/noto/internal/noto/e2e_noto_test.go b/domains/noto/internal/noto/e2e_noto_test.go index 4d2fc716c..d249c714c 100644 --- a/domains/noto/internal/noto/e2e_noto_test.go +++ b/domains/noto/internal/noto/e2e_noto_test.go @@ -170,7 +170,7 @@ func TestNoto(t *testing.T) { } assert.True(t, boolResult) - coins, err := noto.FindCoins(ctx, notoAddress.String(), "{}") + coins, err := noto.FindCoins(ctx, notoAddress, "{}") require.NoError(t, err) require.Len(t, coins, 1) assert.Equal(t, int64(100), coins[0].Amount.Int64()) @@ -217,7 +217,7 @@ func TestNoto(t *testing.T) { require.NoError(t, rpcerr.Error()) } - coins, err = noto.FindCoins(ctx, notoAddress.String(), "{}") + coins, err = noto.FindCoins(ctx, notoAddress, "{}") require.NoError(t, err) require.Len(t, coins, 3) @@ -246,7 +246,7 @@ func TestNoto(t *testing.T) { require.NoError(t, rpcerr.Error()) } - coins, err = noto.FindCoins(ctx, notoAddress.String(), "{}") + coins, err = noto.FindCoins(ctx, notoAddress, "{}") require.NoError(t, err) require.Len(t, coins, 4) // TODO: verify coins } @@ -405,7 +405,7 @@ func TestNotoSelfSubmit(t *testing.T) { } assert.True(t, boolResult) - coins, err := noto.FindCoins(ctx, notoAddress.String(), "{}") + coins, err := noto.FindCoins(ctx, notoAddress, "{}") require.NoError(t, err) assert.Len(t, coins, 1) assert.Equal(t, int64(100), coins[0].Amount.Int64()) @@ -425,7 +425,7 @@ func TestNotoSelfSubmit(t *testing.T) { require.NoError(t, rpcerr.Error()) } - coins, err = noto.FindCoins(ctx, notoAddress.String(), "{}") + coins, err = noto.FindCoins(ctx, notoAddress, "{}") require.NoError(t, err) assert.Len(t, coins, 3) // TODO: verify coins @@ -443,7 +443,7 @@ func TestNotoSelfSubmit(t *testing.T) { require.NoError(t, rpcerr.Error()) } - coins, err = noto.FindCoins(ctx, notoAddress.String(), "{}") + coins, err = noto.FindCoins(ctx, notoAddress, "{}") require.NoError(t, err) assert.Len(t, coins, 4) // TODO: verify coins } diff --git a/domains/noto/internal/noto/noto.go b/domains/noto/internal/noto/noto.go index f16cb2f30..18668807b 100644 --- a/domains/noto/internal/noto/noto.go +++ b/domains/noto/internal/noto/noto.go @@ -325,8 +325,8 @@ func (n *Noto) gatherCoins(ctx context.Context, inputs, outputs []*pb.Endorsable }, nil } -func (n *Noto) FindCoins(ctx context.Context, contractAddress, query string) ([]*types.NotoCoin, error) { - states, err := n.findAvailableStates(ctx, contractAddress, query) +func (n *Noto) FindCoins(ctx context.Context, contractAddress ethtypes.Address0xHex, query string) ([]*types.NotoCoin, error) { + states, err := n.findAvailableStates(ctx, contractAddress.String(), query) if err != nil { return nil, err } diff --git a/domains/noto/pkg/noto/noto.go b/domains/noto/pkg/noto/noto.go index 7767fa9d8..6300c190e 100644 --- a/domains/noto/pkg/noto/noto.go +++ b/domains/noto/pkg/noto/noto.go @@ -18,6 +18,7 @@ package noto import ( "context" + "github.com/hyperledger/firefly-signer/pkg/ethtypes" internal "github.com/kaleido-io/paladin/domains/noto/internal/noto" "github.com/kaleido-io/paladin/domains/noto/pkg/types" "github.com/kaleido-io/paladin/toolkit/pkg/plugintk" @@ -26,7 +27,7 @@ import ( type Noto interface { plugintk.DomainAPI GetHandler(method string) types.DomainHandler - FindCoins(ctx context.Context, contractAddress, query string) ([]*types.NotoCoin, error) + FindCoins(ctx context.Context, contractAddress ethtypes.Address0xHex, query string) ([]*types.NotoCoin, error) } func New(callbacks plugintk.DomainCallbacks) Noto { diff --git a/domains/zeto/integration-test/e2e_test.go b/domains/zeto/integration-test/e2e_test.go index d4a49a7e7..b2df83f01 100644 --- a/domains/zeto/integration-test/e2e_test.go +++ b/domains/zeto/integration-test/e2e_test.go @@ -205,7 +205,7 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string) { } assert.True(t, boolResult) - coins, err := s.domain.FindCoins(ctx, zetoAddress.String(), "{}") + coins, err := s.domain.FindCoins(ctx, zetoAddress, "{}") require.NoError(t, err) require.Len(t, coins, 1) assert.Equal(t, int64(10), coins[0].Amount.Int64()) @@ -226,7 +226,7 @@ func (s *zetoDomainTestSuite) testZetoFungible(t *testing.T, tokenName string) { } assert.True(t, boolResult) - coins, err = s.domain.FindCoins(ctx, zetoAddress.String(), "{}") + coins, err = s.domain.FindCoins(ctx, zetoAddress, "{}") require.NoError(t, err) require.Len(t, coins, 2) assert.Equal(t, int64(10), coins[0].Amount.Int64()) diff --git a/domains/zeto/internal/zeto/zeto.go b/domains/zeto/internal/zeto/zeto.go index dde62ebdc..45beab0b4 100644 --- a/domains/zeto/internal/zeto/zeto.go +++ b/domains/zeto/internal/zeto/zeto.go @@ -239,8 +239,8 @@ func (z *Zeto) validateTransaction(ctx context.Context, tx *pb.TransactionSpecif }, handler, nil } -func (z *Zeto) FindCoins(ctx context.Context, contractAddress, query string) ([]*types.ZetoCoin, error) { - states, err := z.findAvailableStates(ctx, contractAddress, query) +func (z *Zeto) FindCoins(ctx context.Context, contractAddress ethtypes.Address0xHex, query string) ([]*types.ZetoCoin, error) { + states, err := z.findAvailableStates(ctx, contractAddress.String(), query) if err != nil { return nil, err } diff --git a/domains/zeto/pkg/zeto/zeto.go b/domains/zeto/pkg/zeto/zeto.go index 0c5bacb8d..7c89e53e6 100644 --- a/domains/zeto/pkg/zeto/zeto.go +++ b/domains/zeto/pkg/zeto/zeto.go @@ -18,6 +18,7 @@ package zeto import ( "context" + "github.com/hyperledger/firefly-signer/pkg/ethtypes" internal "github.com/kaleido-io/paladin/domains/zeto/internal/zeto" "github.com/kaleido-io/paladin/domains/zeto/pkg/types" "github.com/kaleido-io/paladin/toolkit/pkg/plugintk" @@ -26,7 +27,7 @@ import ( type Zeto interface { plugintk.DomainAPI GetHandler(method string) types.DomainHandler - FindCoins(ctx context.Context, contractAddress, query string) ([]*types.ZetoCoin, error) + FindCoins(ctx context.Context, contractAddress ethtypes.Address0xHex, query string) ([]*types.ZetoCoin, error) } func New(callbacks plugintk.DomainCallbacks) Zeto { From 62cdcdea4cff09d5c8c05788a8c90200aa34f304 Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 19 Sep 2024 15:45:28 -0400 Subject: [PATCH 17/26] creating jar file Signed-off-by: dwertent --- build.gradle | 2 +- core/java/build.gradle | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 09fe58979..551fe0c96 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,7 @@ task buildGoProjects { } // Build Java project after Go projects are done -task buildJavaProject(dependsOn: [buildGoProjects, ':core:java:build']) +task buildJavaProject(dependsOn: [buildGoProjects, ':core:java:build', ':core:java:fullJar']) // Root build task, depends on Go and Java builds task build { diff --git a/core/java/build.gradle b/core/java/build.gradle index da7ea6a7c..f5319e36e 100644 --- a/core/java/build.gradle +++ b/core/java/build.gradle @@ -8,15 +8,33 @@ plugins { group = 'io.kaleido' -jar { +// Set the main class for the JAR +// do not call the 'jar' task becasue pente is using it. call fullJar instead +task fullJar(type: Jar) { + manifest { + attributes( + 'Main-Class': 'io.kaleido.paladin.Main' + ) + } + if (project.hasProperty('jarVersion')) { archiveVersion = project.jarVersion } else { - // Default version - archiveVersion = '' + archiveVersion = '' } archiveBaseName = 'paladin' // Base name of the JAR, the version will be added to this + + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } + + from sourceSets.main.output + + // Exclude signature files from dependencies + exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA' + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } task wrapper(type: Wrapper){} @@ -147,6 +165,7 @@ protobuf { sourceSets { main { java { + srcDirs 'src/main/java' srcDirs 'build/generated/source/proto/main/grpc' srcDirs 'build/generated/source/proto/main/java' } From dd3fa18ba9bcdea5a5633041d28774163afd0993 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 19 Sep 2024 16:31:51 -0400 Subject: [PATCH 18/26] Address Go routine memory access issue that caused #164 by simplifying startup Signed-off-by: Peter Broadhurst --- core/go/internal/msgs/en_errors.go | 1 - core/go/pkg/blockindexer/event_streams.go | 70 +++--------- .../go/pkg/blockindexer/event_streams_test.go | 101 ------------------ 3 files changed, 17 insertions(+), 155 deletions(-) diff --git a/core/go/internal/msgs/en_errors.go b/core/go/internal/msgs/en_errors.go index f4e594e00..0949580ce 100644 --- a/core/go/internal/msgs/en_errors.go +++ b/core/go/internal/msgs/en_errors.go @@ -163,7 +163,6 @@ var ( MsgBlockIndexerESAlreadyInit = ffe("PD011304", "Event stream already initialized") MsgBlockIndexerConfirmedReceiptNotFound = ffe("PD011305", "Receipt for confirmed transaction %s not found") MsgBlockIndexerInvalidEventStreamType = ffe("PD011306", "Unsupported event stream type: %s") - MsgBlockMissingHandler = ffe("PD011307", "Handler not registered for stream") MsgBlockIndexerNoBlocksIndexed = ffe("PD011308", "No confirmed blocks have yet been indexed") MsgBlockIndexerTransactionReverted = ffe("PD011309", "Transaction reverted: %s") diff --git a/core/go/pkg/blockindexer/event_streams.go b/core/go/pkg/blockindexer/event_streams.go index 48973a95a..a0a04c52f 100644 --- a/core/go/pkg/blockindexer/event_streams.go +++ b/core/go/pkg/blockindexer/event_streams.go @@ -18,7 +18,6 @@ package blockindexer import ( "context" - "sync" "time" "github.com/google/uuid" @@ -45,8 +44,6 @@ type eventStream struct { batchTimeout time.Duration blocks chan *eventStreamBlock dispatch chan *eventDispatch - handlerLock sync.Mutex - waitForHandler chan struct{} handler InternalStreamCallback detectorDone chan struct{} dispatcherDone chan struct{} @@ -88,7 +85,7 @@ func (bi *blockIndexer) loadEventStreams(ctx context.Context) error { } for _, esDefinition := range eventStreams { - bi.initEventStream(esDefinition) + bi.initEventStream(esDefinition, nil /* no handler at this point */) } return nil } @@ -160,25 +157,13 @@ func (bi *blockIndexer) upsertInternalEventStream(ctx context.Context, ies *Inte // We call init here // TODO: Full stop/start lifecycle - es := bi.initEventStream(def) - - // Register the internal handler against the new or existing stream - es.attachHandler(ies.Handler) + es := bi.initEventStream(def, ies.Handler) return es, nil } -func (es *eventStream) attachHandler(handler InternalStreamCallback) { - es.handlerLock.Lock() - prevHandler := es.handler - es.handler = handler - if prevHandler == nil { - close(es.waitForHandler) - } - es.handlerLock.Unlock() -} - -func (bi *blockIndexer) initEventStream(definition *EventStream) *eventStream { +// Note that the event stream must be stopped when this is called +func (bi *blockIndexer) initEventStream(definition *EventStream, handler InternalStreamCallback) *eventStream { bi.eventStreamsLock.Lock() defer bi.eventStreamsLock.Unlock() @@ -190,13 +175,12 @@ func (bi *blockIndexer) initEventStream(definition *EventStream) *eventStream { es.definition.Config = definition.Config } else { es = &eventStream{ - bi: bi, - definition: definition, - eventABIs: []*abi.Entry{}, - signatures: make(map[string]bool), - blocks: make(chan *eventStreamBlock, bi.esBlockDispatchQueueLength), - dispatch: make(chan *eventDispatch, batchSize), - waitForHandler: make(chan struct{}), + bi: bi, + definition: definition, + eventABIs: []*abi.Entry{}, + signatures: make(map[string]bool), + blocks: make(chan *eventStreamBlock, bi.esBlockDispatchQueueLength), + dispatch: make(chan *eventDispatch, batchSize), } } @@ -204,6 +188,9 @@ func (bi *blockIndexer) initEventStream(definition *EventStream) *eventStream { es.batchSize = batchSize es.batchTimeout = confutil.DurationMin(definition.Config.BatchTimeout, 0, *EventStreamDefaults.BatchTimeout) + // Note the handler will be nil when this is first called on startup before we've been passed handlers. + es.handler = handler + // Calculate all the signatures we require for _, abiEntry := range definition.ABI { if abiEntry.Type == abi.Event { @@ -231,30 +218,15 @@ func (bi *blockIndexer) startEventStreams() { } func (es *eventStream) start() { - if es.detectorDone == nil && es.dispatcherDone == nil { + if es.handler != nil && es.detectorDone == nil && es.dispatcherDone == nil { es.ctx, es.cancelCtx = context.WithCancel(log.WithLogField(es.bi.parentCtxForReset, "eventstream", es.definition.ID.String())) es.detectorDone = make(chan struct{}) es.dispatcherDone = make(chan struct{}) - es.run() + go es.detector() + go es.dispatcher() } } -func (es *eventStream) run() { - - select { - case <-es.waitForHandler: - case <-es.ctx.Done(): - log.L(es.ctx).Debugf("stopping before event handler registered") - close(es.detectorDone) - close(es.dispatcherDone) - return - } - - go es.detector() - go es.dispatcher() - -} - func (es *eventStream) stop() { if es.cancelCtx != nil { es.cancelCtx() @@ -475,15 +447,7 @@ func (es *eventStream) runBatch(batch *eventBatch) error { return es.bi.retry.Do(es.ctx, func(attempt int) (retryable bool, err error) { var postCommit PostCommit err = es.bi.persistence.DB().Transaction(func(tx *gorm.DB) (err error) { - - es.handlerLock.Lock() - handler := es.handler - es.handlerLock.Unlock() - - if handler == nil { - return i18n.NewError(es.ctx, msgs.MsgBlockMissingHandler) - } - postCommit, err = handler(es.ctx, tx, &batch.EventDeliveryBatch) + postCommit, err = es.handler(es.ctx, tx, &batch.EventDeliveryBatch) if err != nil { return err } diff --git a/core/go/pkg/blockindexer/event_streams_test.go b/core/go/pkg/blockindexer/event_streams_test.go index 9d1b91761..8f5c3e97a 100644 --- a/core/go/pkg/blockindexer/event_streams_test.go +++ b/core/go/pkg/blockindexer/event_streams_test.go @@ -649,107 +649,6 @@ func TestDispatcherDispatchClosed(t *testing.T) { assert.True(t, called) } -func TestDispatcherRunLateHandler(t *testing.T) { - ctx, bi, _, p, done := newMockBlockIndexer(t, &Config{}) - defer done() - - p.Mock.ExpectBegin() - p.Mock.ExpectRollback() - - called := false - - bi.retry.UTSetMaxAttempts(1) - es := &eventStream{ - bi: bi, - ctx: ctx, - definition: &EventStream{ - ID: uuid.New(), - Type: tktypes.Enum[EventStreamType]("wrong"), - ABI: testABI, - }, - eventABIs: testABI, - batchSize: 2, // aim for two - batchTimeout: 1 * time.Microsecond, // but not going to wait - dispatch: make(chan *eventDispatch), - dispatcherDone: make(chan struct{}), - detectorDone: make(chan struct{}), - waitForHandler: make(chan struct{}), - } - go func() { - assert.NotPanics(t, func() { es.run() }) - }() - - time.Sleep(1 * time.Millisecond) - es.attachHandler(func(ctx context.Context, tx *gorm.DB, batch *EventDeliveryBatch) (PostCommit, error) { - called = true - return nil, fmt.Errorf("pop") - }) - - es.dispatch <- &eventDispatch{ - event: &EventWithData{ - IndexedEvent: &IndexedEvent{}, - }, - } - - <-es.dispatcherDone - - assert.True(t, called) -} - -func TestDispatcherRunMissingHandler(t *testing.T) { - ctx, bi, _, p, done := newMockBlockIndexer(t, &Config{}) - defer done() - - p.Mock.ExpectBegin() - p.Mock.ExpectRollback() - - bi.retry.UTSetMaxAttempts(1) - es := &eventStream{ - bi: bi, - ctx: ctx, - definition: &EventStream{ - ID: uuid.New(), - Type: tktypes.Enum[EventStreamType]("wrong"), - ABI: testABI, - }, - eventABIs: testABI, - batchSize: 2, // aim for two - batchTimeout: 1 * time.Microsecond, // but not going to wait - dispatch: make(chan *eventDispatch), - dispatcherDone: make(chan struct{}), - waitForHandler: make(chan struct{}), - } - close(es.waitForHandler) - - go func() { - assert.NotPanics(t, func() { es.dispatcher() }) - }() - - es.dispatch <- &eventDispatch{ - event: &EventWithData{ - IndexedEvent: &IndexedEvent{}, - }, - } - - <-es.dispatcherDone -} - -func TestDispatcherCloseBeforeHandler(t *testing.T) { - ctx, bi, _, _, done := newMockBlockIndexer(t, &Config{}) - defer done() - - cancelledCtx, cancelCtx := context.WithCancel(ctx) - cancelCtx() - es := &eventStream{ - bi: bi, - ctx: cancelledCtx, - waitForHandler: make(chan struct{}), - detectorDone: make(chan struct{}), - dispatcherDone: make(chan struct{}), - } - es.run() -} - func TestProcessCatchupEventPageFailRPC(t *testing.T) { ctx, bi, mRPC, p, done := newMockBlockIndexer(t, &Config{}) defer done() From 92c2292bb051c1dcc1f4ee7bdfc5ba5d5ef768b8 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 19 Sep 2024 18:09:02 -0400 Subject: [PATCH 19/26] Add more debug and copy source during init Signed-off-by: Peter Broadhurst --- core/go/pkg/blockindexer/event_streams.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/go/pkg/blockindexer/event_streams.go b/core/go/pkg/blockindexer/event_streams.go index a0a04c52f..401177ee0 100644 --- a/core/go/pkg/blockindexer/event_streams.go +++ b/core/go/pkg/blockindexer/event_streams.go @@ -85,7 +85,7 @@ func (bi *blockIndexer) loadEventStreams(ctx context.Context) error { } for _, esDefinition := range eventStreams { - bi.initEventStream(esDefinition, nil /* no handler at this point */) + bi.initEventStream(ctx, esDefinition, nil /* no handler at this point */) } return nil } @@ -157,13 +157,13 @@ func (bi *blockIndexer) upsertInternalEventStream(ctx context.Context, ies *Inte // We call init here // TODO: Full stop/start lifecycle - es := bi.initEventStream(def, ies.Handler) + es := bi.initEventStream(ctx, def, ies.Handler) return es, nil } // Note that the event stream must be stopped when this is called -func (bi *blockIndexer) initEventStream(definition *EventStream, handler InternalStreamCallback) *eventStream { +func (bi *blockIndexer) initEventStream(ctx context.Context, definition *EventStream, handler InternalStreamCallback) *eventStream { bi.eventStreamsLock.Lock() defer bi.eventStreamsLock.Unlock() @@ -191,7 +191,15 @@ func (bi *blockIndexer) initEventStream(definition *EventStream, handler Interna // Note the handler will be nil when this is first called on startup before we've been passed handlers. es.handler = handler + location := "*" + if definition.Source != nil { + source := *definition.Source + es.definition.Source = &source + location = source.String() + } + // Calculate all the signatures we require + sigStrings := []string{} for _, abiEntry := range definition.ABI { if abiEntry.Type == abi.Event { sig := tktypes.NewBytes32FromSlice(abiEntry.SignatureHashBytes()) @@ -199,10 +207,12 @@ func (bi *blockIndexer) initEventStream(definition *EventStream, handler Interna es.eventABIs = append(es.eventABIs, abiEntry) if _, dup := es.signatures[sigStr]; !dup { es.signatures[sigStr] = true + sigStrings = append(sigStrings, sigStr) es.signatureList = append(es.signatureList, sig) } } } + log.L(ctx).Infof("Event stream %s configured on sourceMatch=%s signatures: %s", es.definition.ID, location, sigStrings) // ok - all looks good, put ourselves in the blockindexer list bi.eventStreams[definition.ID] = es From 515aed276febf953638fac42b2b04a868685962e Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 19 Sep 2024 18:37:51 -0400 Subject: [PATCH 20/26] More simplification and debug Signed-off-by: Peter Broadhurst --- core/go/pkg/blockindexer/block_indexer.go | 16 ++++--- core/go/pkg/blockindexer/event_streams.go | 20 ++++----- .../go/pkg/blockindexer/event_streams_test.go | 42 +++++++++++++++++++ 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/core/go/pkg/blockindexer/block_indexer.go b/core/go/pkg/blockindexer/block_indexer.go index eb37c60f4..56978f2ae 100644 --- a/core/go/pkg/blockindexer/block_indexer.go +++ b/core/go/pkg/blockindexer/block_indexer.go @@ -853,20 +853,23 @@ func (bi *blockIndexer) enrichTransactionEvents(ctx context.Context, abi abi.ABI } // Spin through the logs to find the corresponding result entries - for _, l := range receipt.Logs { - for _, e := range events { + for _, e := range events { + for _, l := range receipt.Logs { if ethtypes.HexUint64(e.LogIndex) == l.LogIndex { - bi.matchLog(ctx, abi, l, e, nil) + // We decode the data if possible (sets .Data on each event if there's a match) + if bi.matchLog(ctx, abi, l, e, nil) { + break // next log + } } } } return nil } -func (bi *blockIndexer) matchLog(ctx context.Context, abi abi.ABI, in *LogJSONRPC, out *EventWithData, source *tktypes.EthAddress) { +func (bi *blockIndexer) matchLog(ctx context.Context, abi abi.ABI, in *LogJSONRPC, out *EventWithData, source *tktypes.EthAddress) bool { if !source.IsZero() && !source.Equals((*tktypes.EthAddress)(in.Address)) { log.L(ctx).Debugf("Event %d/%d/%d does not match source=%s (tx=%s,address=%s)", in.BlockNumber, in.TransactionIndex, in.LogIndex, source, in.TransactionHash, in.Address) - return + return false } // This is one that matches our signature, but we need to check it against our ABI list. // We stop at the first entry that parses it, and it's perfectly fine and expected that @@ -883,9 +886,10 @@ func (bi *blockIndexer) matchLog(ctx context.Context, abi abi.ABI, in *LogJSONRP if in.Address != nil { out.Address = tktypes.EthAddress(*in.Address) } - return + return true } else { log.L(ctx).Debugf("Event %d/%d/%d does not match ABI event %s matchSource=%v (tx=%s,address=%s): %s", in.BlockNumber, in.TransactionIndex, in.LogIndex, abiEntry, source, in.TransactionHash, in.Address, err) } } + return false } diff --git a/core/go/pkg/blockindexer/event_streams.go b/core/go/pkg/blockindexer/event_streams.go index 401177ee0..f7c0481ab 100644 --- a/core/go/pkg/blockindexer/event_streams.go +++ b/core/go/pkg/blockindexer/event_streams.go @@ -192,14 +192,12 @@ func (bi *blockIndexer) initEventStream(ctx context.Context, definition *EventSt es.handler = handler location := "*" - if definition.Source != nil { - source := *definition.Source - es.definition.Source = &source - location = source.String() + if es.definition.Source != nil { + location = es.definition.Source.String() } // Calculate all the signatures we require - sigStrings := []string{} + solStrings := []string{} for _, abiEntry := range definition.ABI { if abiEntry.Type == abi.Event { sig := tktypes.NewBytes32FromSlice(abiEntry.SignatureHashBytes()) @@ -207,12 +205,12 @@ func (bi *blockIndexer) initEventStream(ctx context.Context, definition *EventSt es.eventABIs = append(es.eventABIs, abiEntry) if _, dup := es.signatures[sigStr]; !dup { es.signatures[sigStr] = true - sigStrings = append(sigStrings, sigStr) + solStrings = append(solStrings, abiEntry.SolString()) es.signatureList = append(es.signatureList, sig) } } } - log.L(ctx).Infof("Event stream %s configured on sourceMatch=%s signatures: %s", es.definition.ID, location, sigStrings) + log.L(ctx).Infof("Event stream %s configured matchSource=%s events: %s", es.definition.ID, location, solStrings) // ok - all looks good, put ourselves in the blockindexer list bi.eventStreams[definition.ID] = es @@ -370,13 +368,13 @@ func (es *eventStream) detector() { } func (es *eventStream) processNotifiedBlock(block *eventStreamBlock, fullBlock bool) { + matchSource := es.definition.Source for i, l := range block.events { event := &EventWithData{ IndexedEvent: es.bi.logToIndexedEvent(l), } - es.matchLog(l, event) // Only dispatch events that were completed by the validation against our ABI - if event.Data != nil { + if es.bi.matchLog(es.ctx, es.eventABIs, l, event, matchSource) { es.sendToDispatcher(event, // Can only move checkpoint past this block once we know we've processed the last one fullBlock && i == (len(block.events)-1)) @@ -574,7 +572,3 @@ func (es *eventStream) processCatchupEventPage(checkpointBlock int64, catchUpToB func (es *eventStream) queryTransactionEvents(tx tktypes.Bytes32, events []*EventWithData, done chan error) { done <- es.bi.enrichTransactionEvents(es.ctx, es.eventABIs, tx, events, true /* retry indefinitely */) } - -func (es *eventStream) matchLog(in *LogJSONRPC, out *EventWithData) { - es.bi.matchLog(es.ctx, es.eventABIs, in, out, es.definition.Source) -} diff --git a/core/go/pkg/blockindexer/event_streams_test.go b/core/go/pkg/blockindexer/event_streams_test.go index 8f5c3e97a..3ce9151e9 100644 --- a/core/go/pkg/blockindexer/event_streams_test.go +++ b/core/go/pkg/blockindexer/event_streams_test.go @@ -304,6 +304,48 @@ func TestInternalEventStreamDeliveryCatchUp(t *testing.T) { } } +func TestNoMatchingEvents(t *testing.T) { + + // This test uses a real DB, includes the full block indexer, but simulates the blockchain. + _, bi, mRPC, blDone := newTestBlockIndexer(t) + defer blDone() + + // Mock up the block calls to the blockchain for 15 blocks + blocks, receipts := testBlockArray(t, 15) + mockBlocksRPCCalls(mRPC, blocks, receipts) + mockBlockListenerNil(mRPC) + + // Create a matcher that only mismatched on indexed - so same signature + testABICopy := testParseABI(testEventABIJSON) + testABICopy[1].Inputs[0].Indexed = !testABICopy[1].Inputs[0].Indexed + + // Do a full start now with an internal event listener + err := bi.Start(&InternalEventStream{ + Handler: func(ctx context.Context, tx *gorm.DB, batch *EventDeliveryBatch) (PostCommit, error) { + require.Fail(t, "should not be called") + return nil, nil + }, + Definition: &EventStream{ + Name: "unit_test", + Config: EventStreamConfig{ + BatchSize: confutil.P(1), + BatchTimeout: confutil.P("5ms"), + }, + // Listen to two out of three event types + ABI: abi.ABI{ + // Mismatched only on index + testABICopy[1], + }, + }, + }) + require.NoError(t, err) + + for i := 0; i < 15; i++ { + <-bi.utBatchNotify + } + +} + func TestStartBadInternalEventStream(t *testing.T) { // This test uses a real DB, includes the full block indexer, but simulates the blockchain. From 4fb709a1344430b8736a1b0ddca6afbbcd6c184c Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 19 Sep 2024 18:42:44 -0400 Subject: [PATCH 21/26] Revert order Signed-off-by: Peter Broadhurst --- core/go/pkg/blockindexer/block_indexer.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/core/go/pkg/blockindexer/block_indexer.go b/core/go/pkg/blockindexer/block_indexer.go index 56978f2ae..ba12b142d 100644 --- a/core/go/pkg/blockindexer/block_indexer.go +++ b/core/go/pkg/blockindexer/block_indexer.go @@ -853,13 +853,12 @@ func (bi *blockIndexer) enrichTransactionEvents(ctx context.Context, abi abi.ABI } // Spin through the logs to find the corresponding result entries - for _, e := range events { - for _, l := range receipt.Logs { + for _, l := range receipt.Logs { + for _, e := range events { if ethtypes.HexUint64(e.LogIndex) == l.LogIndex { - // We decode the data if possible (sets .Data on each event if there's a match) - if bi.matchLog(ctx, abi, l, e, nil) { - break // next log - } + // This the the log for this event - try and enrich the .Data field + _ = bi.matchLog(ctx, abi, l, e, nil) + break } } } From 1fa1a3a2e822de2aff641c28668e6aed41b5a054 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 19 Sep 2024 19:30:38 -0400 Subject: [PATCH 22/26] More debug Signed-off-by: Peter Broadhurst --- core/go/pkg/blockindexer/event_streams.go | 5 ++++ .../go/pkg/blockindexer/event_streams_test.go | 28 ++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/core/go/pkg/blockindexer/event_streams.go b/core/go/pkg/blockindexer/event_streams.go index f7c0481ab..bf563385e 100644 --- a/core/go/pkg/blockindexer/event_streams.go +++ b/core/go/pkg/blockindexer/event_streams.go @@ -221,6 +221,7 @@ func (bi *blockIndexer) startEventStreams() { bi.eventStreamsLock.Lock() defer bi.eventStreamsLock.Unlock() for _, es := range bi.eventStreams { + log.L(es.ctx).Infof("Starting event stream %s", es.definition.ID) es.start() } } @@ -292,6 +293,8 @@ func (bi *blockIndexer) getHighestIndexedBlock(ctx context.Context) (*int64, err func (es *eventStream) detector() { defer close(es.detectorDone) + log.L(es.ctx).Debugf("Detector started for event stream %s", es.definition.ID) + // This routine reads the checkpoint on startup, and maintains its view in memory, // but never writes it back. // The checkpoint is updated on the dispatcher after each batch is confirmed downstream. @@ -393,6 +396,8 @@ func (es *eventStream) sendToDispatcher(event *EventWithData, lastInBlock bool) func (es *eventStream) dispatcher() { defer close(es.dispatcherDone) + log.L(es.ctx).Debugf("Dispatcher started for event stream %s", es.definition.ID) + l := log.L(es.ctx) var batch *eventBatch for { diff --git a/core/go/pkg/blockindexer/event_streams_test.go b/core/go/pkg/blockindexer/event_streams_test.go index 3ce9151e9..61e4cab37 100644 --- a/core/go/pkg/blockindexer/event_streams_test.go +++ b/core/go/pkg/blockindexer/event_streams_test.go @@ -142,6 +142,20 @@ func TestInternalEventStreamDeliveryAtHeadWithSourceAddress(t *testing.T) { eventCollector := make(chan *EventWithData) + definition := &EventStream{ + Name: "unit_test", + Config: EventStreamConfig{ + BatchSize: confutil.P(3), + BatchTimeout: confutil.P("5ms"), + }, + // Listen to two out of three event types + ABI: abi.ABI{ + testABI[1], + testABI[2], + }, + Source: sourceContractAddress, + } + // Do a full start now with an internal event listener var esID string calledPostCommit := false @@ -163,19 +177,7 @@ func TestInternalEventStreamDeliveryAtHeadWithSourceAddress(t *testing.T) { } return func() { calledPostCommit = true }, nil }, - Definition: &EventStream{ - Name: "unit_test", - Config: EventStreamConfig{ - BatchSize: confutil.P(3), - BatchTimeout: confutil.P("5ms"), - }, - // Listen to two out of three event types - ABI: abi.ABI{ - testABI[1], - testABI[2], - }, - Source: sourceContractAddress, - }, + Definition: definition, }) require.NoError(t, err) From 4c720b8fbf2db9c003541ee0198070e241a42d9e Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 19 Sep 2024 19:37:21 -0400 Subject: [PATCH 23/26] More debug Signed-off-by: Peter Broadhurst --- core/go/pkg/blockindexer/event_streams.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/go/pkg/blockindexer/event_streams.go b/core/go/pkg/blockindexer/event_streams.go index bf563385e..e2ec1d480 100644 --- a/core/go/pkg/blockindexer/event_streams.go +++ b/core/go/pkg/blockindexer/event_streams.go @@ -221,7 +221,7 @@ func (bi *blockIndexer) startEventStreams() { bi.eventStreamsLock.Lock() defer bi.eventStreamsLock.Unlock() for _, es := range bi.eventStreams { - log.L(es.ctx).Infof("Starting event stream %s", es.definition.ID) + log.L(bi.parentCtxForReset).Infof("Starting event stream %s [%s]", es.definition.Name, es.definition.ID) es.start() } } @@ -293,7 +293,7 @@ func (bi *blockIndexer) getHighestIndexedBlock(ctx context.Context) (*int64, err func (es *eventStream) detector() { defer close(es.detectorDone) - log.L(es.ctx).Debugf("Detector started for event stream %s", es.definition.ID) + log.L(es.ctx).Debugf("Detector started for event stream %s [%s]", es.definition.Name, es.definition.ID) // This routine reads the checkpoint on startup, and maintains its view in memory, // but never writes it back. @@ -396,7 +396,7 @@ func (es *eventStream) sendToDispatcher(event *EventWithData, lastInBlock bool) func (es *eventStream) dispatcher() { defer close(es.dispatcherDone) - log.L(es.ctx).Debugf("Dispatcher started for event stream %s", es.definition.ID) + log.L(es.ctx).Debugf("Dispatcher started for event stream %s [%s]", es.definition.Name, es.definition.ID) l := log.L(es.ctx) var batch *eventBatch From 3f451e0da01499e69ed0b9330c7036b9fbce87f7 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 19 Sep 2024 19:54:14 -0400 Subject: [PATCH 24/26] Avoiding holding eventStreamsLock while starting go routines Signed-off-by: Peter Broadhurst --- core/go/pkg/blockindexer/event_streams.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/go/pkg/blockindexer/event_streams.go b/core/go/pkg/blockindexer/event_streams.go index e2ec1d480..5cbfb15f1 100644 --- a/core/go/pkg/blockindexer/event_streams.go +++ b/core/go/pkg/blockindexer/event_streams.go @@ -217,11 +217,18 @@ func (bi *blockIndexer) initEventStream(ctx context.Context, definition *EventSt return es } -func (bi *blockIndexer) startEventStreams() { +func (bi *blockIndexer) getStreamList() []*eventStream { bi.eventStreamsLock.Lock() defer bi.eventStreamsLock.Unlock() + streams := make([]*eventStream, 0, len(bi.eventStreams)) for _, es := range bi.eventStreams { - log.L(bi.parentCtxForReset).Infof("Starting event stream %s [%s]", es.definition.Name, es.definition.ID) + streams = append(streams, es) + } + return streams +} + +func (bi *blockIndexer) startEventStreams() { + for _, es := range bi.getStreamList() { es.start() } } @@ -229,6 +236,7 @@ func (bi *blockIndexer) startEventStreams() { func (es *eventStream) start() { if es.handler != nil && es.detectorDone == nil && es.dispatcherDone == nil { es.ctx, es.cancelCtx = context.WithCancel(log.WithLogField(es.bi.parentCtxForReset, "eventstream", es.definition.ID.String())) + log.L(es.ctx).Infof("Starting event stream %s [%s]", es.definition.Name, es.definition.ID) es.detectorDone = make(chan struct{}) es.dispatcherDone = make(chan struct{}) go es.detector() From 882dad84c6c4bbca08eba863e1ba2e8a1548836e Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 19 Sep 2024 22:04:45 -0400 Subject: [PATCH 25/26] Allow errors during register that close stream so send fails Signed-off-by: Peter Broadhurst --- core/go/internal/plugins/plugin_base_test.go | 16 +++++++++++----- core/go/internal/plugins/transports_test.go | 7 ++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/core/go/internal/plugins/plugin_base_test.go b/core/go/internal/plugins/plugin_base_test.go index 90ec6fc9a..0b86e0834 100644 --- a/core/go/internal/plugins/plugin_base_test.go +++ b/core/go/internal/plugins/plugin_base_test.go @@ -35,10 +35,11 @@ import ( type mockPlugin[T any] struct { t *testing.T - conf *components.PluginConfig - preRegister func(domainID string) *T - customResponses func(*T) []*T - expectClose func(err error) + conf *components.PluginConfig + allowRegisterErrors bool + preRegister func(domainID string) *T + customResponses func(*T) []*T + expectClose func(err error) headerAccessor func(*T) *prototk.Header connectFactory func(ctx context.Context, client prototk.PluginControllerClient) (grpc.BidiStreamingClient[T, T], error) @@ -81,7 +82,12 @@ func (tp *mockPlugin[T]) Run(grpcTarget, pluginId string) { header.MessageId = uuid.New().String() header.MessageType = prototk.Header_REGISTER err = stream.Send(regMsg) - require.NoError(t, err) + if err != nil { + if tp.allowRegisterErrors { + return + } + require.NoError(t, err) + } // Switch to stream conect ctx := stream.Context() diff --git a/core/go/internal/plugins/transports_test.go b/core/go/internal/plugins/transports_test.go index abd5111ec..fd57b6e81 100644 --- a/core/go/internal/plugins/transports_test.go +++ b/core/go/internal/plugins/transports_test.go @@ -193,9 +193,10 @@ func TestTransportRegisterFail(t *testing.T) { tdm := &testTransportManager{ transports: map[string]plugintk.Plugin{ "transport1": &mockPlugin[prototk.TransportMessage]{ - t: t, - connectFactory: transportConnectFactory, - headerAccessor: transportHeaderAccessor, + t: t, + allowRegisterErrors: true, + connectFactory: transportConnectFactory, + headerAccessor: transportHeaderAccessor, preRegister: func(transportID string) *prototk.TransportMessage { return &prototk.TransportMessage{ Header: &prototk.Header{ From 97f963e8009d608eb2be6503740d7f990bd7d403 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Thu, 19 Sep 2024 23:54:50 -0400 Subject: [PATCH 26/26] Propagate "source" when processing catchup pages Signed-off-by: Andrew Richardson --- core/go/pkg/blockindexer/block_indexer.go | 6 +++--- core/go/pkg/blockindexer/event_streams.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/go/pkg/blockindexer/block_indexer.go b/core/go/pkg/blockindexer/block_indexer.go index ba12b142d..c8a119008 100644 --- a/core/go/pkg/blockindexer/block_indexer.go +++ b/core/go/pkg/blockindexer/block_indexer.go @@ -825,7 +825,7 @@ func (bi *blockIndexer) DecodeTransactionEvents(ctx context.Context, hash tktype for i, event := range events { decoded[i] = &EventWithData{IndexedEvent: event} } - err = bi.enrichTransactionEvents(ctx, abi, hash, decoded, false /* no retry */) + err = bi.enrichTransactionEvents(ctx, abi, nil, hash, decoded, false /* no retry */) return decoded, err } @@ -841,7 +841,7 @@ func (bi *blockIndexer) getConfirmedTransactionReceipt(ctx context.Context, tx e return receipt, nil } -func (bi *blockIndexer) enrichTransactionEvents(ctx context.Context, abi abi.ABI, tx tktypes.Bytes32, events []*EventWithData, indefiniteRetry bool) error { +func (bi *blockIndexer) enrichTransactionEvents(ctx context.Context, abi abi.ABI, source *tktypes.EthAddress, tx tktypes.Bytes32, events []*EventWithData, indefiniteRetry bool) error { // Get the TX receipt with all the logs var receipt *TXReceiptJSONRPC err := bi.retry.Do(ctx, func(attempt int) (_ bool, err error) { @@ -857,7 +857,7 @@ func (bi *blockIndexer) enrichTransactionEvents(ctx context.Context, abi abi.ABI for _, e := range events { if ethtypes.HexUint64(e.LogIndex) == l.LogIndex { // This the the log for this event - try and enrich the .Data field - _ = bi.matchLog(ctx, abi, l, e, nil) + _ = bi.matchLog(ctx, abi, l, e, source) break } } diff --git a/core/go/pkg/blockindexer/event_streams.go b/core/go/pkg/blockindexer/event_streams.go index 5cbfb15f1..676bbdd2c 100644 --- a/core/go/pkg/blockindexer/event_streams.go +++ b/core/go/pkg/blockindexer/event_streams.go @@ -583,5 +583,5 @@ func (es *eventStream) processCatchupEventPage(checkpointBlock int64, catchUpToB } func (es *eventStream) queryTransactionEvents(tx tktypes.Bytes32, events []*EventWithData, done chan error) { - done <- es.bi.enrichTransactionEvents(es.ctx, es.eventABIs, tx, events, true /* retry indefinitely */) + done <- es.bi.enrichTransactionEvents(es.ctx, es.eventABIs, es.definition.Source, tx, events, true /* retry indefinitely */) }