diff --git a/api/jsonrpc/server.go b/api/jsonrpc/server.go index 361ca9513d..a2bdca155a 100644 --- a/api/jsonrpc/server.go +++ b/api/jsonrpc/server.go @@ -204,7 +204,7 @@ func (j *JSONRPCServer) ExecuteActions( for actionIndex, action := range actions { // Get expected state keys - stateKeysWithPermissions := action.StateKeys(args.Actor) + stateKeysWithPermissions := action.StateKeys(args.Actor, chain.CreateActionID(ids.Empty, uint8(actionIndex))) // flatten the map to a slice of keys storageKeysToRead := make([][]byte, 0, len(stateKeysWithPermissions)) diff --git a/chain/dependencies.go b/chain/dependencies.go index ed94bbcd1a..a69809d160 100644 --- a/chain/dependencies.go +++ b/chain/dependencies.go @@ -207,8 +207,11 @@ type Action interface { // All keys specified must be suffixed with the number of chunks that could ever be read from that // key (formatted as a big-endian uint16). This is used to automatically calculate storage usage. // - // If any key is removed and then re-created, this will count as a creation instead of a modification. - StateKeys(actor codec.Address) state.Keys + // If any key is removed and then re-created, this will count as a creation + // instead of a modification. + // + // [actionID] is a unique, but nonrandom identifier for each [Action]. + StateKeys(actor codec.Address, actionID ids.ID) state.Keys // Execute actually runs the [Action]. Any state changes that the [Action] performs should // be done here. @@ -216,7 +219,10 @@ type Action interface { // If any keys are touched during [Execute] that are not specified in [StateKeys], the transaction // will revert and the max fee will be charged. // - // If [Execute] returns an error, execution will halt and any state changes will revert. + // If [Execute] returns an error, execution will halt and any state changes + // will revert. + // + // [actionID] is a unique, but nonrandom identifier for each [Action]. Execute( ctx context.Context, r Rules, diff --git a/chain/transaction.go b/chain/transaction.go index e4b301a236..a0908d4bd6 100644 --- a/chain/transaction.go +++ b/chain/transaction.go @@ -169,8 +169,8 @@ func (t *Transaction) StateKeys(bh BalanceHandler) (state.Keys, error) { stateKeys := make(state.Keys) // Verify the formatting of state keys passed by the controller - for _, action := range t.Actions { - for k, v := range action.StateKeys(t.Auth.Actor()) { + for i, action := range t.Actions { + for k, v := range action.StateKeys(t.Auth.Actor(), CreateActionID(t.ID(), uint8(i))) { if !stateKeys.Add(k, v) { return nil, ErrInvalidKeyValue } @@ -517,14 +517,14 @@ func EstimateUnits(r Rules, actions Actions, authFactory AuthFactory) (fees.Dime // Calculate over action/auth bandwidth += consts.Uint8Len - for _, action := range actions { + for i, action := range actions { actionSize, err := GetSize(action) if err != nil { return fees.Dimensions{}, err } actor := authFactory.Address() - stateKeys := action.StateKeys(actor) + stateKeys := action.StateKeys(actor, CreateActionID(ids.Empty, uint8(i))) actionStateKeysMaxChunks, ok := stateKeys.ChunkSizes() if !ok { return fees.Dimensions{}, ErrInvalidKeyValue diff --git a/chain/transaction_test.go b/chain/transaction_test.go index 9c12ec8cc5..d00ed512c4 100644 --- a/chain/transaction_test.go +++ b/chain/transaction_test.go @@ -32,7 +32,7 @@ func (*abstractMockAction) Execute(_ context.Context, _ chain.Rules, _ state.Mut panic("unimplemented") } -func (*abstractMockAction) StateKeys(_ codec.Address) state.Keys { +func (*abstractMockAction) StateKeys(_ codec.Address, _ ids.ID) state.Keys { panic("unimplemented") } diff --git a/examples/morpheusvm/actions/transfer.go b/examples/morpheusvm/actions/transfer.go index 57cd253201..c0a2965805 100644 --- a/examples/morpheusvm/actions/transfer.go +++ b/examples/morpheusvm/actions/transfer.go @@ -43,7 +43,7 @@ func (*Transfer) GetTypeID() uint8 { return mconsts.TransferID } -func (t *Transfer) StateKeys(actor codec.Address) state.Keys { +func (t *Transfer) StateKeys(actor codec.Address, _ ids.ID) state.Keys { return state.Keys{ string(storage.BalanceKey(actor)): state.Read | state.Write, string(storage.BalanceKey(t.To)): state.All, diff --git a/examples/vmwithcontracts/actions/call.go b/examples/vmwithcontracts/actions/call.go index 60271a94c5..0261fde0a4 100644 --- a/examples/vmwithcontracts/actions/call.go +++ b/examples/vmwithcontracts/actions/call.go @@ -55,7 +55,7 @@ func (*Call) GetTypeID() uint8 { return mconsts.CallContractID } -func (t *Call) StateKeys(_ codec.Address) state.Keys { +func (t *Call) StateKeys(_ codec.Address, _ ids.ID) state.Keys { result := state.Keys{} for _, stateKeyPermission := range t.SpecifiedStateKeys { result.Add(stateKeyPermission.Key, stateKeyPermission.Permission) diff --git a/examples/vmwithcontracts/actions/deploy.go b/examples/vmwithcontracts/actions/deploy.go index a53a677c20..418d0ba2a1 100644 --- a/examples/vmwithcontracts/actions/deploy.go +++ b/examples/vmwithcontracts/actions/deploy.go @@ -33,7 +33,7 @@ func (*Deploy) GetTypeID() uint8 { return mconsts.DeployID } -func (d *Deploy) StateKeys(_ codec.Address) state.Keys { +func (d *Deploy) StateKeys(_ codec.Address, _ ids.ID) state.Keys { if d.address == codec.EmptyAddress { d.address = storage.GetAddressForDeploy(0, d.CreationInfo) } diff --git a/examples/vmwithcontracts/actions/publish.go b/examples/vmwithcontracts/actions/publish.go index 1b664e8272..70b5fc7881 100644 --- a/examples/vmwithcontracts/actions/publish.go +++ b/examples/vmwithcontracts/actions/publish.go @@ -33,7 +33,7 @@ func (*Publish) GetTypeID() uint8 { return mconsts.PublishID } -func (t *Publish) StateKeys(_ codec.Address) state.Keys { +func (t *Publish) StateKeys(_ codec.Address, _ ids.ID) state.Keys { if t.id == nil { hashedID := sha256.Sum256(t.ContractBytes) t.id, _ = keys.Encode(storage.ContractsKey(hashedID[:]), len(t.ContractBytes)) diff --git a/examples/vmwithcontracts/actions/transfer.go b/examples/vmwithcontracts/actions/transfer.go index 44398f0b96..aff7918d54 100644 --- a/examples/vmwithcontracts/actions/transfer.go +++ b/examples/vmwithcontracts/actions/transfer.go @@ -44,7 +44,7 @@ func (*Transfer) GetTypeID() uint8 { return mconsts.TransferID } -func (t *Transfer) StateKeys(actor codec.Address) state.Keys { +func (t *Transfer) StateKeys(actor codec.Address, _ ids.ID) state.Keys { return state.Keys{ string(storage.BalanceKey(actor)): state.Read | state.Write, string(storage.BalanceKey(t.To)): state.All,