From f5e7097ccc0214b8e5c16206f5f5c9e49a093877 Mon Sep 17 00:00:00 2001 From: Silas Davis <silas@monax.io> Date: Tue, 27 Mar 2018 21:02:38 +0100 Subject: [PATCH] Reload callee and caller after CALLs and update both on create. Move state to sub-package fixing cyclic dependency. No longer expect insufficient gas for nested call. Signed-off-by: Silas Davis <silas@monax.io> --- account/account.go | 10 +- account/account_test.go | 11 ++ account/state.go | 73 ------------ account/state/state.go | 101 ++++++++++++++++ account/{ => state}/state_cache.go | 58 ++++----- account/state/state_cache_test.go | 129 +++++++++++++++++++++ account/state_cache_test.go | 63 ---------- client/rpc/client.go | 3 +- execution/evm/accounts.go | 4 +- execution/evm/fake_app_state.go | 3 +- execution/evm/native.go | 10 +- execution/evm/snative.go | 63 +++++----- execution/evm/vm.go | 86 +++++++++----- execution/evm/vm_test.go | 33 +++--- execution/execution.go | 71 ++++++------ execution/execution_test.go | 40 ++++--- execution/state.go | 10 +- execution/state_test.go | 12 +- execution/transactor.go | 11 +- permission/constants.go | 10 -- permission/permissions.go | 19 --- permission/{ => snatives}/snatives.go | 43 +++---- permission/{ => snatives}/snatives_test.go | 9 +- permission/util.go | 2 +- rpc/service.go | 21 ++-- txs/tx.go | 4 +- txs/tx_test.go | 5 +- txs/tx_utils.go | 15 +-- 28 files changed, 525 insertions(+), 394 deletions(-) delete mode 100644 account/state.go create mode 100644 account/state/state.go rename account/{ => state}/state_cache.go (76%) create mode 100644 account/state/state_cache_test.go delete mode 100644 account/state_cache_test.go delete mode 100644 permission/constants.go rename permission/{ => snatives}/snatives.go (72%) rename permission/{ => snatives}/snatives_test.go (65%) diff --git a/account/account.go b/account/account.go index 9ed538c5..ab6e9874 100644 --- a/account/account.go +++ b/account/account.go @@ -25,6 +25,8 @@ import ( "github.com/tendermint/go-wire" ) +var GlobalPermissionsAddress = Address(binary.Zero160) + // Signable is an interface for all signable things. // It typically removes signatures before serializing. type Signable interface { @@ -216,14 +218,6 @@ func AsMutableAccount(account Account) MutableAccount { return AsConcreteAccount(account).MutableAccount() } -func GetMutableAccount(getter Getter, address Address) (MutableAccount, error) { - acc, err := getter.GetAccount(address) - if err != nil { - return nil, err - } - return AsMutableAccount(acc), nil -} - //---------------------------------------------- // concreteAccount Wrapper diff --git a/account/account_test.go b/account/account_test.go index 800ea4ff..aaffb2ff 100644 --- a/account/account_test.go +++ b/account/account_test.go @@ -21,6 +21,8 @@ import ( "fmt" + "github.com/hyperledger/burrow/permission" + "github.com/hyperledger/burrow/permission/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/go-crypto" @@ -77,11 +79,20 @@ func TestAccountSerialise(t *testing.T) { func TestDecodeConcrete(t *testing.T) { concreteAcc := NewConcreteAccountFromSecret("Super Semi Secret") + concreteAcc.Permissions = types.AccountPermissions{ + Base: types.BasePermissions{ + Perms: permission.SetGlobal, + SetBit: permission.SetGlobal, + }, + Roles: []string{"bums"}, + } acc := concreteAcc.Account() encodedAcc, err := acc.Encode() require.NoError(t, err) + concreteAccOut, err := DecodeConcrete(encodedAcc) require.NoError(t, err) + assert.Equal(t, concreteAcc, *concreteAccOut) concreteAccOut, err = DecodeConcrete([]byte("flungepliffery munknut tolopops")) assert.Error(t, err) diff --git a/account/state.go b/account/state.go deleted file mode 100644 index 5569fbe6..00000000 --- a/account/state.go +++ /dev/null @@ -1,73 +0,0 @@ -package account - -import ( - "github.com/hyperledger/burrow/binary" -) - -type Getter interface { - // Get an account by its address return nil if it does not exist (which should not be an error) - GetAccount(address Address) (Account, error) -} - -type Iterable interface { - // Iterates through accounts calling passed function once per account, if the consumer - // returns true the iteration breaks and returns true to indicate it iteration - // was escaped - IterateAccounts(consumer func(Account) (stop bool)) (stopped bool, err error) -} - -type Updater interface { - // Updates the fields of updatedAccount by address, creating the account - // if it does not exist - UpdateAccount(updatedAccount Account) error - // Remove the account at address - RemoveAccount(address Address) error -} - -type StorageGetter interface { - // Retrieve a 32-byte value stored at key for the account at address, return Zero256 if key does not exist but - // error if address does not - GetStorage(address Address, key binary.Word256) (value binary.Word256, err error) -} - -type StorageSetter interface { - // Store a 32-byte value at key for the account at address - SetStorage(address Address, key, value binary.Word256) error -} - -type StorageIterable interface { - // Iterates through the storage of account ad address calling the passed function once per account, - // if the iterator function returns true the iteration breaks and returns true to indicate it iteration - // was escaped - IterateStorage(address Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) -} - -// Compositions - -// Read-only account and storage state -type StateReader interface { - Getter - StorageGetter -} - -// Read and list account and storage state -type StateIterable interface { - StateReader - Iterable - StorageIterable -} - -// Read and write account and storage state -type StateWriter interface { - StateReader - Updater - StorageSetter -} - -type IterableStateWriter interface { - StateReader - Updater - StorageSetter - Iterable - StorageIterable -} diff --git a/account/state/state.go b/account/state/state.go new file mode 100644 index 00000000..005d3082 --- /dev/null +++ b/account/state/state.go @@ -0,0 +1,101 @@ +package state + +import ( + acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/binary" + ptypes "github.com/hyperledger/burrow/permission/types" +) + +type AccountGetter interface { + // Get an account by its address return nil if it does not exist (which should not be an error) + GetAccount(address acm.Address) (acm.Account, error) +} + +type AccountIterable interface { + // Iterates through accounts calling passed function once per account, if the consumer + // returns true the iteration breaks and returns true to indicate it iteration + // was escaped + IterateAccounts(consumer func(acm.Account) (stop bool)) (stopped bool, err error) +} + +type AccountUpdater interface { + // Updates the fields of updatedAccount by address, creating the account + // if it does not exist + UpdateAccount(updatedAccount acm.Account) error + // Remove the account at address + RemoveAccount(address acm.Address) error +} + +type StorageGetter interface { + // Retrieve a 32-byte value stored at key for the account at address, return Zero256 if key does not exist but + // error if address does not + GetStorage(address acm.Address, key binary.Word256) (value binary.Word256, err error) +} + +type StorageSetter interface { + // Store a 32-byte value at key for the account at address + SetStorage(address acm.Address, key, value binary.Word256) error +} + +type StorageIterable interface { + // Iterates through the storage of account ad address calling the passed function once per account, + // if the iterator function returns true the iteration breaks and returns true to indicate it iteration + // was escaped + IterateStorage(address acm.Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) +} + +// Compositions + +// Read-only account and storage state +type Reader interface { + AccountGetter + StorageGetter +} + +// Read and list account and storage state +type Iterable interface { + Reader + AccountIterable + StorageIterable +} + +// Read and write account and storage state +type Writer interface { + Reader + AccountUpdater + StorageSetter +} + +type IterableWriter interface { + Reader + AccountUpdater + StorageSetter + AccountIterable + StorageIterable +} + +func GetMutableAccount(getter AccountGetter, address acm.Address) (acm.MutableAccount, error) { + acc, err := getter.GetAccount(address) + if err != nil { + return nil, err + } + return acm.AsMutableAccount(acc), nil +} + +func GlobalPermissionsAccount(getter AccountGetter) acm.Account { + acc, err := getter.GetAccount(acm.GlobalPermissionsAddress) + if err != nil { + panic("Could not get global permission account, but this must exist") + } + return acc +} + +// Get global permissions from the account at GlobalPermissionsAddress +func GlobalAccountPermissions(getter AccountGetter) ptypes.AccountPermissions { + if getter == nil { + return ptypes.AccountPermissions{ + Roles: []string{}, + } + } + return GlobalPermissionsAccount(getter).Permissions() +} diff --git a/account/state_cache.go b/account/state/state_cache.go similarity index 76% rename from account/state_cache.go rename to account/state/state_cache.go index 28b6d0c5..f9d85c97 100644 --- a/account/state_cache.go +++ b/account/state/state_cache.go @@ -12,48 +12,49 @@ // See the License for the specific language governing permissions and // limitations under the License. -package account +package state import ( "fmt" "sort" "sync" + acm "github.com/hyperledger/burrow/account" "github.com/hyperledger/burrow/binary" ) -type StateCache interface { - IterableStateWriter - Sync(state StateWriter) error - Reset(backend StateIterable) - Flush(state IterableStateWriter) error - Backend() StateIterable +type Cache interface { + IterableWriter + Sync(state Writer) error + Reset(backend Iterable) + Flush(state IterableWriter) error + Backend() Iterable } type stateCache struct { sync.RWMutex - backend StateIterable - accounts map[Address]*accountInfo + backend Iterable + accounts map[acm.Address]*accountInfo } type accountInfo struct { sync.RWMutex - account Account + account acm.Account storage map[binary.Word256]binary.Word256 removed bool updated bool } -// Returns a StateCache that wraps an underlying StateReader to use on a cache miss, can write to an output StateWriter +// Returns a Cache that wraps an underlying Reader to use on a cache miss, can write to an output Writer // via Sync. Goroutine safe for concurrent access. -func NewStateCache(backend StateIterable) StateCache { +func NewCache(backend Iterable) Cache { return &stateCache{ backend: backend, - accounts: make(map[Address]*accountInfo), + accounts: make(map[acm.Address]*accountInfo), } } -func (cache *stateCache) GetAccount(address Address) (Account, error) { +func (cache *stateCache) GetAccount(address acm.Address) (acm.Account, error) { accInfo, err := cache.get(address) if err != nil { return nil, err @@ -66,7 +67,7 @@ func (cache *stateCache) GetAccount(address Address) (Account, error) { return accInfo.account, nil } -func (cache *stateCache) UpdateAccount(account Account) error { +func (cache *stateCache) UpdateAccount(account acm.Account) error { accInfo, err := cache.get(account.Address()) if err != nil { return err @@ -78,10 +79,11 @@ func (cache *stateCache) UpdateAccount(account Account) error { } accInfo.account = account accInfo.updated = true + return nil } -func (cache *stateCache) RemoveAccount(address Address) error { +func (cache *stateCache) RemoveAccount(address acm.Address) error { accInfo, err := cache.get(address) if err != nil { return err @@ -96,7 +98,7 @@ func (cache *stateCache) RemoveAccount(address Address) error { } // Iterates over all accounts first in cache and then in backend until consumer returns true for 'stop' -func (cache *stateCache) IterateAccounts(consumer func(Account) (stop bool)) (stopped bool, err error) { +func (cache *stateCache) IterateAccounts(consumer func(acm.Account) (stop bool)) (stopped bool, err error) { // Try cache first for early exit cache.RLock() for _, info := range cache.accounts { @@ -108,7 +110,7 @@ func (cache *stateCache) IterateAccounts(consumer func(Account) (stop bool)) (st return cache.backend.IterateAccounts(consumer) } -func (cache *stateCache) GetStorage(address Address, key binary.Word256) (binary.Word256, error) { +func (cache *stateCache) GetStorage(address acm.Address, key binary.Word256) (binary.Word256, error) { accInfo, err := cache.get(address) if err != nil { return binary.Zero256, err @@ -133,7 +135,7 @@ func (cache *stateCache) GetStorage(address Address, key binary.Word256) (binary } // NOTE: Set value to zero to remove. -func (cache *stateCache) SetStorage(address Address, key binary.Word256, value binary.Word256) error { +func (cache *stateCache) SetStorage(address acm.Address, key binary.Word256, value binary.Word256) error { accInfo, err := cache.get(address) accInfo.Lock() defer accInfo.Unlock() @@ -149,7 +151,7 @@ func (cache *stateCache) SetStorage(address Address, key binary.Word256, value b } // Iterates over all storage items first in cache and then in backend until consumer returns true for 'stop' -func (cache *stateCache) IterateStorage(address Address, +func (cache *stateCache) IterateStorage(address acm.Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) { accInfo, err := cache.get(address) if err != nil { @@ -168,10 +170,10 @@ func (cache *stateCache) IterateStorage(address Address, // Syncs changes to the backend in deterministic order. Sends storage updates before updating // the account they belong so that storage values can be taken account of in the update. -func (cache *stateCache) Sync(state StateWriter) error { +func (cache *stateCache) Sync(state Writer) error { cache.Lock() defer cache.Unlock() - var addresses Addresses + var addresses acm.Addresses for address := range cache.accounts { addresses = append(addresses, address) } @@ -212,15 +214,15 @@ func (cache *stateCache) Sync(state StateWriter) error { } // Resets the cache to empty initialising the backing map to the same size as the previous iteration. -func (cache *stateCache) Reset(backend StateIterable) { +func (cache *stateCache) Reset(backend Iterable) { cache.Lock() defer cache.Unlock() cache.backend = backend - cache.accounts = make(map[Address]*accountInfo, len(cache.accounts)) + cache.accounts = make(map[acm.Address]*accountInfo, len(cache.accounts)) } -// Syncs the StateCache and Resets it to use as the backend StateReader -func (cache *stateCache) Flush(state IterableStateWriter) error { +// Syncs the Cache and Resets it to use as the backend Reader +func (cache *stateCache) Flush(state IterableWriter) error { err := cache.Sync(state) if err != nil { return err @@ -229,12 +231,12 @@ func (cache *stateCache) Flush(state IterableStateWriter) error { return nil } -func (cache *stateCache) Backend() StateIterable { +func (cache *stateCache) Backend() Iterable { return cache.backend } // Get the cache accountInfo item creating it if necessary -func (cache *stateCache) get(address Address) (*accountInfo, error) { +func (cache *stateCache) get(address acm.Address) (*accountInfo, error) { cache.RLock() accInfo := cache.accounts[address] cache.RUnlock() diff --git a/account/state/state_cache_test.go b/account/state/state_cache_test.go new file mode 100644 index 00000000..5928a6ed --- /dev/null +++ b/account/state/state_cache_test.go @@ -0,0 +1,129 @@ +package state + +import ( + "fmt" + "testing" + + acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/binary" + "github.com/hyperledger/burrow/permission" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStateCache_GetAccount(t *testing.T) { + acc := acm.NewConcreteAccountFromSecret("foo") + acc.Permissions.Base.Perms = permission.AddRole | permission.Send + acc.Permissions.Base.SetBit = acc.Permissions.Base.Perms + state := combine(account(acc.Account(), "I AM A KEY", "NO YOU ARE A KEY")) + cache := NewCache(state) + + accOut, err := cache.GetAccount(acc.Address) + require.NoError(t, err) + cache.UpdateAccount(accOut) + accEnc, err := acc.Encode() + accEncOut, err := accOut.Encode() + assert.Equal(t, accEnc, accEncOut) + assert.Equal(t, acc.Permissions, accOut.Permissions()) + + cacheBackend := NewCache(newTestState()) + err = cache.Sync(cacheBackend) + require.NoError(t, err) + accOut, err = cacheBackend.GetAccount(acc.Address) + require.NotNil(t, accOut) + accEncOut, err = accOut.Encode() + assert.NoError(t, err) + assert.Equal(t, accEnc, accEncOut) +} + +func TestStateCache_UpdateAccount(t *testing.T) { +} + +func TestStateCache_RemoveAccount(t *testing.T) { +} + +func TestStateCache_GetStorage(t *testing.T) { +} + +func TestStateCache_SetStorage(t *testing.T) { +} + +func TestStateCache_Sync(t *testing.T) { +} + +func TestStateCache_get(t *testing.T) { +} + +// TODO: write tests as part of feature branch +type testState struct { + Accounts map[acm.Address]acm.Account + Storage map[acm.Address]map[binary.Word256]binary.Word256 +} + +var _ Iterable = &testState{} + +func newTestState() *testState { + return &testState{ + Accounts: make(map[acm.Address]acm.Account), + Storage: make(map[acm.Address]map[binary.Word256]binary.Word256), + } +} + +func account(acc acm.Account, keyvals ...string) *testState { + ts := newTestState() + ts.Accounts[acc.Address()] = acc + ts.Storage[acc.Address()] = make(map[binary.Word256]binary.Word256) + for i := 0; i < len(keyvals); i += 2 { + ts.Storage[acc.Address()][word(keyvals[i])] = word(keyvals[i+1]) + } + return ts +} + +func combine(states ...*testState) *testState { + ts := newTestState() + for _, state := range states { + for _, acc := range state.Accounts { + ts.Accounts[acc.Address()] = acc + ts.Storage[acc.Address()] = state.Storage[acc.Address()] + } + } + return ts +} + +func word(str string) binary.Word256 { + return binary.LeftPadWord256([]byte(str)) +} + +func (tsr *testState) IterateAccounts(consumer func(acm.Account) (stop bool)) (stopped bool, err error) { + for _, acc := range tsr.Accounts { + if consumer(acc) { + return true, nil + } + } + return false, nil +} + +func (tsr *testState) IterateStorage(address acm.Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error) { + for key, value := range tsr.Storage[address] { + if consumer(key, value) { + return true, nil + } + } + return false, nil +} + +func (tsr *testState) GetAccount(address acm.Address) (acm.Account, error) { + return tsr.Accounts[address], nil +} + +func (tsr *testState) GetStorage(address acm.Address, key binary.Word256) (binary.Word256, error) { + storage, ok := tsr.Storage[address] + if !ok { + return binary.Zero256, fmt.Errorf("could not find storage for account %s", address) + } + value, ok := storage[key] + if !ok { + return binary.Zero256, fmt.Errorf("could not find key %x for account %s", key, address) + } + return value, nil +} diff --git a/account/state_cache_test.go b/account/state_cache_test.go deleted file mode 100644 index 6ecc2cdd..00000000 --- a/account/state_cache_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package account - -import ( - "testing" - - "fmt" - - "github.com/hyperledger/burrow/binary" -) - -// TODO: write tests as part of feature branch -type testStateReader struct { - Accounts map[Address]Account - Storage map[Address]map[binary.Word256]binary.Word256 -} - -func accountAndStorage(account Account, keyvals ...binary.Word256) *testStateReader { - return &testStateReader{} - -} - -func (tsr *testStateReader) GetAccount(address Address) (Account, error) { - account, ok := tsr.Accounts[address] - if !ok { - return nil, fmt.Errorf("could not find account %s", address) - } - return account, nil -} - -func (tsr *testStateReader) GetStorage(address Address, key binary.Word256) (binary.Word256, error) { - storage, ok := tsr.Storage[address] - if !ok { - return binary.Zero256, fmt.Errorf("could not find storage for account %s", address) - } - value, ok := storage[key] - if !ok { - return binary.Zero256, fmt.Errorf("could not find key %x for account %s", key, address) - } - return value, nil -} - -var _ StateReader = &testStateReader{} - -func TestStateCache_GetAccount(t *testing.T) { -} - -func TestStateCache_UpdateAccount(t *testing.T) { -} - -func TestStateCache_RemoveAccount(t *testing.T) { -} - -func TestStateCache_GetStorage(t *testing.T) { -} - -func TestStateCache_SetStorage(t *testing.T) { -} - -func TestStateCache_Sync(t *testing.T) { -} - -func TestStateCache_get(t *testing.T) { -} diff --git a/client/rpc/client.go b/client/rpc/client.go index 53d80dbc..3a76948c 100644 --- a/client/rpc/client.go +++ b/client/rpc/client.go @@ -24,6 +24,7 @@ import ( acm "github.com/hyperledger/burrow/account" "github.com/hyperledger/burrow/client" "github.com/hyperledger/burrow/keys" + "github.com/hyperledger/burrow/permission/snatives" "github.com/hyperledger/burrow/txs" ) @@ -114,7 +115,7 @@ func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, if err != nil { return nil, fmt.Errorf("could not convert action '%s' to PermFlag: %v", action, err) } - permArgs := ptypes.PermArgs{ + permArgs := snatives.PermArgs{ PermFlag: permFlag, } diff --git a/execution/evm/accounts.go b/execution/evm/accounts.go index 4f6ce54c..800e6269 100644 --- a/execution/evm/accounts.go +++ b/execution/evm/accounts.go @@ -22,11 +22,11 @@ func DeriveNewAccount(creator acm.MutableAccount, permissions ptypes.AccountPerm addr := acm.NewContractAddress(creator.Address(), sequence) // Create account from address. - return (&acm.ConcreteAccount{ + return acm.ConcreteAccount{ Address: addr, Balance: 0, Code: nil, Sequence: 0, Permissions: permissions, - }).MutableAccount() + }.MutableAccount() } diff --git a/execution/evm/fake_app_state.go b/execution/evm/fake_app_state.go index 09a985ca..7efbe82c 100644 --- a/execution/evm/fake_app_state.go +++ b/execution/evm/fake_app_state.go @@ -20,6 +20,7 @@ import ( "bytes" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" . "github.com/hyperledger/burrow/binary" ) @@ -28,7 +29,7 @@ type FakeAppState struct { storage map[string]Word256 } -var _ acm.StateWriter = &FakeAppState{} +var _ state.Writer = &FakeAppState{} func (fas *FakeAppState) GetAccount(addr acm.Address) (acm.Account, error) { account := fas.accounts[addr] diff --git a/execution/evm/native.go b/execution/evm/native.go index 3bf07619..70f00ed6 100644 --- a/execution/evm/native.go +++ b/execution/evm/native.go @@ -18,6 +18,7 @@ import ( "crypto/sha256" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" . "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/logging" "golang.org/x/crypto/ripemd160" @@ -53,7 +54,7 @@ func registerNativeContracts() { //----------------------------------------------------------------------------- -type NativeContract func(state acm.StateWriter, caller acm.Account, input []byte, gas *uint64, +type NativeContract func(state state.Writer, caller acm.Account, input []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) /* Removed due to C dependency @@ -73,13 +74,14 @@ func ecrecoverFunc(state State, caller *acm.Account, input []byte, gas *int64) ( recovered, err := secp256k1.RecoverPubkey(hash, sig) if err != nil { return nil, err +OH NO STOCASTIC CAT CODING!!!! } hashed := sha3.Sha3(recovered[1:]) return LeftPadBytes(hashed, 32), nil } */ -func sha256Func(state acm.StateWriter, caller acm.Account, input []byte, gas *uint64, +func sha256Func(state state.Writer, caller acm.Account, input []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { // Deduct gas gasRequired := uint64((len(input)+31)/32)*GasSha256Word + GasSha256Base @@ -95,7 +97,7 @@ func sha256Func(state acm.StateWriter, caller acm.Account, input []byte, gas *ui return hasher.Sum(nil), nil } -func ripemd160Func(state acm.StateWriter, caller acm.Account, input []byte, gas *uint64, +func ripemd160Func(state state.Writer, caller acm.Account, input []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { // Deduct gas gasRequired := uint64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base @@ -111,7 +113,7 @@ func ripemd160Func(state acm.StateWriter, caller acm.Account, input []byte, gas return LeftPadBytes(hasher.Sum(nil), 32), nil } -func identityFunc(state acm.StateWriter, caller acm.Account, input []byte, gas *uint64, +func identityFunc(state state.Writer, caller acm.Account, input []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { // Deduct gas gasRequired := uint64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase diff --git a/execution/evm/snative.go b/execution/evm/snative.go index a172aedc..34375a0a 100644 --- a/execution/evm/snative.go +++ b/execution/evm/snative.go @@ -20,6 +20,7 @@ import ( "strings" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" . "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/execution/evm/abi" "github.com/hyperledger/burrow/execution/evm/sha3" @@ -225,10 +226,19 @@ func NewSNativeContract(comment, name string, } } +type ErrLacksSNativePermission struct { + Address acm.Address + SNative string +} + +func (e ErrLacksSNativePermission) Error() string { + return fmt.Sprintf("account %s does not have SNative function call permission: %s", e.Address, e.SNative) +} + // This function is designed to be called from the EVM once a SNative contract // has been selected. It is also placed in a registry by registerSNativeContracts // So it can be looked up by SNative address -func (contract *SNativeContractDescription) Dispatch(state acm.StateWriter, caller acm.Account, +func (contract *SNativeContractDescription) Dispatch(state state.Writer, caller acm.Account, args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { logger = logger.With(structure.ScopeKey, "Dispatch", "contract_name", contract.Name) @@ -340,7 +350,7 @@ func abiReturn(name string, abiTypeName abi.TypeName) abi.Return { // Permission function defintions // TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) -func hasBase(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64, +func hasBase(state state.Writer, caller acm.Account, args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { addrWord256, permNum := returnTwoArgs(args) @@ -366,12 +376,12 @@ func hasBase(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64 return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func setBase(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64, +func setBase(stateWriter state.Writer, caller acm.Account, args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { addrWord256, permNum, permVal := returnThreeArgs(args) address := acm.AddressFromWord256(addrWord256) - acc, err := acm.GetMutableAccount(state, address) + acc, err := state.GetMutableAccount(stateWriter, address) if err != nil { return nil, err } @@ -386,19 +396,19 @@ func setBase(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64 if err = acc.MutablePermissions().Base.Set(permN, permV); err != nil { return nil, err } - state.UpdateAccount(acc) + stateWriter.UpdateAccount(acc) logger.Trace.Log("function", "setBase", "address", address.String(), "permission_flag", fmt.Sprintf("%b", permN), "permission_value", permV) - return effectivePermBytes(acc.Permissions().Base, globalPerms(state)), nil + return effectivePermBytes(acc.Permissions().Base, globalPerms(stateWriter)), nil } -func unsetBase(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64, +func unsetBase(stateWriter state.Writer, caller acm.Account, args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { addrWord256, permNum := returnTwoArgs(args) address := acm.AddressFromWord256(addrWord256) - acc, err := acm.GetMutableAccount(state, address) + acc, err := state.GetMutableAccount(stateWriter, address) if err != nil { return nil, err } @@ -412,19 +422,19 @@ func unsetBase(state acm.StateWriter, caller acm.Account, args []byte, gas *uint if err = acc.MutablePermissions().Base.Unset(permN); err != nil { return nil, err } - state.UpdateAccount(acc) + stateWriter.UpdateAccount(acc) logger.Trace.Log("function", "unsetBase", "address", address.String(), "perm_flag", fmt.Sprintf("%b", permN), "permission_flag", fmt.Sprintf("%b", permN)) - return effectivePermBytes(acc.Permissions().Base, globalPerms(state)), nil + return effectivePermBytes(acc.Permissions().Base, globalPerms(stateWriter)), nil } -func setGlobal(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64, +func setGlobal(stateWriter state.Writer, caller acm.Account, args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { permNum, permVal := returnTwoArgs(args) - acc, err := acm.GetMutableAccount(state, permission.GlobalPermissionsAddress) + acc, err := state.GetMutableAccount(stateWriter, acm.GlobalPermissionsAddress) if err != nil { return nil, err } @@ -439,14 +449,14 @@ func setGlobal(state acm.StateWriter, caller acm.Account, args []byte, gas *uint if err = acc.MutablePermissions().Base.Set(permN, permV); err != nil { return nil, err } - state.UpdateAccount(acc) + stateWriter.UpdateAccount(acc) logger.Trace.Log("function", "setGlobal", "permission_flag", fmt.Sprintf("%b", permN), "permission_value", permV) return permBytes(acc.Permissions().Base.ResultantPerms()), nil } -func hasRole(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64, +func hasRole(state state.Writer, caller acm.Account, args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { addrWord256, role := returnTwoArgs(args) @@ -467,12 +477,12 @@ func hasRole(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64 return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func addRole(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64, +func addRole(stateWriter state.Writer, caller acm.Account, args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { addrWord256, role := returnTwoArgs(args) address := acm.AddressFromWord256(addrWord256) - acc, err := acm.GetMutableAccount(state, address) + acc, err := state.GetMutableAccount(stateWriter, address) if err != nil { return nil, err } @@ -482,19 +492,19 @@ func addRole(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64 roleS := string(role.Bytes()) roleAdded := acc.MutablePermissions().AddRole(roleS) permInt := byteFromBool(roleAdded) - state.UpdateAccount(acc) + stateWriter.UpdateAccount(acc) logger.Trace.Log("function", "addRole", "address", address.String(), "role", roleS, "role_added", roleAdded) return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func removeRole(state acm.StateWriter, caller acm.Account, args []byte, gas *uint64, +func removeRole(stateWriter state.Writer, caller acm.Account, args []byte, gas *uint64, logger *logging.Logger) (output []byte, err error) { addrWord256, role := returnTwoArgs(args) address := acm.AddressFromWord256(addrWord256) - acc, err := acm.GetMutableAccount(state, address) + acc, err := state.GetMutableAccount(stateWriter, address) if err != nil { return nil, err } @@ -504,7 +514,7 @@ func removeRole(state acm.StateWriter, caller acm.Account, args []byte, gas *uin roleS := string(role.Bytes()) roleRemoved := acc.MutablePermissions().RmRole(roleS) permInt := byteFromBool(roleRemoved) - state.UpdateAccount(acc) + stateWriter.UpdateAccount(acc) logger.Trace.Log("function", "removeRole", "address", address.String(), "role", roleS, "role_removed", roleRemoved) @@ -514,23 +524,14 @@ func removeRole(state acm.StateWriter, caller acm.Account, args []byte, gas *uin //------------------------------------------------------------------------------------------------ // Errors and utility funcs -type ErrLacksSNativePermission struct { - Address acm.Address - SNative string -} - -func (e ErrLacksSNativePermission) Error() string { - return fmt.Sprintf("account %s does not have SNative function call permission: %s", e.Address, e.SNative) -} - // Checks if a permission flag is valid (a known base chain or snative permission) func ValidPermN(n ptypes.PermFlag) bool { return n <= permission.AllPermFlags } // Get the global BasePermissions -func globalPerms(state acm.StateWriter) ptypes.BasePermissions { - return permission.GlobalAccountPermissions(state).Base +func globalPerms(stateWriter state.Writer) ptypes.BasePermissions { + return state.GlobalAccountPermissions(stateWriter).Base } // Compute the effective permissions from an acm.Account's BasePermissions by diff --git a/execution/evm/vm.go b/execution/evm/vm.go index 9e2e3566..f94cf768 100644 --- a/execution/evm/vm.go +++ b/execution/evm/vm.go @@ -23,6 +23,7 @@ import ( "strings" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" . "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/event" . "github.com/hyperledger/burrow/execution/evm/asm" @@ -102,7 +103,7 @@ type Params struct { } type VM struct { - state acm.StateWriter + stateWriter state.Writer memoryProvider func() Memory params Params origin acm.Address @@ -115,10 +116,10 @@ type VM struct { dumpTokens bool } -func NewVM(state acm.StateWriter, params Params, origin acm.Address, txid []byte, +func NewVM(stateWriter state.Writer, params Params, origin acm.Address, txid []byte, logger *logging.Logger, options ...func(*VM)) *VM { vm := &VM{ - state: state, + stateWriter: stateWriter, memoryProvider: DefaultDynamicMemoryProvider, params: params, origin: origin, @@ -149,8 +150,8 @@ func (vm *VM) SetPublisher(publisher event.Publisher) { // on known permissions and panics else) // If the perm is not defined in the acc nor set by default in GlobalPermissions, // this function returns false. -func HasPermission(state acm.StateWriter, acc acm.Account, perm ptypes.PermFlag) bool { - value, _ := acc.Permissions().Base.Compose(permission.GlobalAccountPermissions(state).Base).Get(perm) +func HasPermission(stateWriter state.Writer, acc acm.Account, perm ptypes.PermFlag) bool { + value, _ := acc.Permissions().Base.Compose(state.GlobalAccountPermissions(stateWriter).Base).Get(perm) return value } @@ -213,7 +214,7 @@ func (vm *VM) Call(caller, callee acm.MutableAccount, code, input []byte, value // The intent of delegate call is to run the code of the callee in the storage context of the caller; // while preserving the original caller to the previous callee. // Different to the normal CALL or CALLCODE, the value does not need to be transferred to the callee. -func (vm *VM) DelegateCall(caller, callee acm.MutableAccount, code, input []byte, value uint64, gas *uint64) (output []byte, err error) { +func (vm *VM) DelegateCall(caller acm.Account, callee acm.MutableAccount, code, input []byte, value uint64, gas *uint64) (output []byte, err error) { exception := new(string) // fire the post call event (including exception if applicable) @@ -248,7 +249,7 @@ func useGasNegative(gasLeft *uint64, gasToUse uint64, err *error) bool { } // Just like Call() but does not transfer 'value' or modify the callDepth. -func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value uint64, gas *uint64) (output []byte, err error) { +func (vm *VM) call(caller acm.Account, callee acm.MutableAccount, code, input []byte, value uint64, gas *uint64) (output []byte, err error) { vm.Debugf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.stackDepth, caller.Address().Bytes()[:4], callee.Address(), len(callee.Code()), *gas, input) @@ -508,7 +509,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasGetAccount, &err) { return nil, err } - acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr)) + acc, errAcc := vm.stateWriter.GetAccount(acm.AddressFromWord256(addr)) if errAcc != nil { return nil, firstErr(err, errAcc) } @@ -604,7 +605,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasGetAccount, &err) { return nil, err } - acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr)) + acc, errAcc := vm.stateWriter.GetAccount(acm.AddressFromWord256(addr)) if errAcc != nil { return nil, firstErr(err, errAcc) } @@ -625,7 +626,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasGetAccount, &err) { return nil, err } - acc, errAcc := vm.state.GetAccount(acm.AddressFromWord256(addr)) + acc, errAcc := vm.stateWriter.GetAccount(acm.AddressFromWord256(addr)) if errAcc != nil { return nil, firstErr(err, errAcc) } @@ -718,7 +719,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value case SLOAD: // 0x54 loc := stack.Pop() - data, errSto := vm.state.GetStorage(callee.Address(), loc) + data, errSto := vm.stateWriter.GetStorage(callee.Address(), loc) if errSto != nil { return nil, firstErr(err, errSto) } @@ -730,7 +731,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasStorageUpdate, &err) { return nil, err } - vm.state.SetStorage(callee.Address(), loc, data) + vm.stateWriter.SetStorage(callee.Address(), loc, data) vm.Debugf("%s {0x%X := 0x%X}\n", callee.Address(), loc, data) case JUMP: // 0x56 @@ -815,8 +816,6 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value return nil, firstErr(err, ErrMemoryOutOfBounds) } if vm.publisher != nil { - eventID := events.EventStringLogEvent(callee.Address()) - fmt.Printf("eventID: %s\n", eventID) events.PublishLogEvent(vm.publisher, callee.Address(), &events.EventDataLog{ Address: callee.Address(), Topics: topics, @@ -827,7 +826,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value vm.Debugf(" => T:%X D:%X\n", topics, data) case CREATE: // 0xF0 - if !HasPermission(vm.state, callee, permission.CreateContract) { + if !HasPermission(vm.stateWriter, callee, permission.CreateContract) { return nil, ErrPermission{"create_contract"} } contractValue, popErr := stack.PopU64() @@ -851,8 +850,10 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasCreateAccount, &gasErr) { return nil, firstErr(err, gasErr) } - newAccount := DeriveNewAccount(callee, permission.GlobalAccountPermissions(vm.state), logger) - vm.state.UpdateAccount(newAccount) + newAccount, createErr := vm.createAccount(callee, logger) + if createErr != nil { + return nil, firstErr(err, createErr) + } // Run the input to get the contract code. // NOTE: no need to copy 'input' as per Call contract. @@ -869,7 +870,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value } case CALL, CALLCODE, DELEGATECALL: // 0xF1, 0xF2, 0xF4 - if !HasPermission(vm.state, callee, permission.Call) { + if !HasPermission(vm.stateWriter, callee, permission.Call) { return nil, ErrPermission{"call"} } gasLimit, popErr := stack.PopU64() @@ -919,7 +920,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if nativeContract := registeredNativeContracts[addr]; nativeContract != nil { // Native contract - ret, callErr = nativeContract(vm.state, callee, args, &gasLimit, vm.logger) + ret, callErr = nativeContract(vm.stateWriter, callee, args, &gasLimit, logger) // for now we fire the Call event. maybe later we'll fire more particulars var exception string @@ -933,7 +934,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasGetAccount, &callErr) { return nil, callErr } - acc, errAcc := acm.GetMutableAccount(vm.state, acm.AddressFromWord256(addr)) + acc, errAcc := state.GetMutableAccount(vm.stateWriter, acm.AddressFromWord256(addr)) if errAcc != nil { return nil, firstErr(callErr, errAcc) } @@ -954,16 +955,27 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value } else { // nil account means we're sending funds to a new account if acc == nil { - if !HasPermission(vm.state, caller, permission.CreateAccount) { + if !HasPermission(vm.stateWriter, caller, permission.CreateAccount) { return nil, ErrPermission{"create_account"} } - acc = (&acm.ConcreteAccount{Address: acm.AddressFromWord256(addr)}).MutableAccount() + acc = acm.ConcreteAccount{Address: acm.AddressFromWord256(addr)}.MutableAccount() } // add account to the tx cache - vm.state.UpdateAccount(acc) + vm.stateWriter.UpdateAccount(acc) ret, callErr = vm.Call(callee, acc, acc.Code(), args, value, &gasLimit) } } + // In case any calls deeper in the stack (particularly SNatives) has altered either of two accounts to which + // we hold a reference, we need to freshen our state for subsequent iterations of this call frame's EVM loop + var getErr error + caller, getErr = vm.stateWriter.GetAccount(caller.Address()) + if getErr != nil { + return nil, firstErr(err, getErr) + } + callee, getErr = state.GetMutableAccount(vm.stateWriter, callee.Address()) + if getErr != nil { + return nil, firstErr(err, getErr) + } // Push result if callErr != nil { @@ -1025,7 +1037,7 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasGetAccount, &err) { return nil, err } - receiver, errAcc := acm.GetMutableAccount(vm.state, acm.AddressFromWord256(addr)) + receiver, errAcc := state.GetMutableAccount(vm.stateWriter, acm.AddressFromWord256(addr)) if errAcc != nil { return nil, firstErr(err, errAcc) } @@ -1034,18 +1046,23 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value if useGasNegative(gas, GasCreateAccount, &gasErr) { return nil, firstErr(err, gasErr) } - if !HasPermission(vm.state, callee, permission.CreateContract) { + if !HasPermission(vm.stateWriter, callee, permission.CreateContract) { return nil, firstErr(err, ErrPermission{"create_contract"}) } - receiver = DeriveNewAccount(callee, permission.GlobalAccountPermissions(vm.state), logger) + var createErr error + receiver, createErr = vm.createAccount(callee, logger) + if createErr != nil { + return nil, firstErr(err, createErr) + } + } receiver, errAdd := receiver.AddToBalance(callee.Balance()) if errAdd != nil { return nil, firstErr(err, errAdd) } - vm.state.UpdateAccount(receiver) - vm.state.RemoveAccount(callee.Address()) + vm.stateWriter.UpdateAccount(receiver) + vm.stateWriter.RemoveAccount(callee.Address()) vm.Debugf(" => (%X) %v\n", addr[:4], callee.Balance()) fallthrough @@ -1062,6 +1079,19 @@ func (vm *VM) call(caller, callee acm.MutableAccount, code, input []byte, value } } +func (vm *VM) createAccount(callee acm.MutableAccount, logger *logging.Logger) (acm.MutableAccount, error) { + newAccount := DeriveNewAccount(callee, state.GlobalAccountPermissions(vm.stateWriter), logger) + err := vm.stateWriter.UpdateAccount(newAccount) + if err != nil { + return nil, err + } + err = vm.stateWriter.UpdateAccount(callee) + if err != nil { + return nil, err + } + return newAccount, nil +} + // TODO: [Silas] this function seems extremely dubious to me. It was being used // in circumstances where its behaviour did not match the intention. It's bounds // check is strange (treats a read at data length as a zero read of arbitrary length) diff --git a/execution/evm/vm_test.go b/execution/evm/vm_test.go index 690f2c79..82d79012 100644 --- a/execution/evm/vm_test.go +++ b/execution/evm/vm_test.go @@ -24,6 +24,7 @@ import ( "time" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" . "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/event" . "github.com/hyperledger/burrow/execution/evm/asm" @@ -38,7 +39,8 @@ import ( ) // Test output is a bit clearer if we /dev/null the logging, but can be re-enabled by uncommenting the below -//var logger, _ = lifecycle.NewStdErrLogger() +//var logger, _, _ = lifecycle.NewStdErrLogger() +// var logger = logging.NewNoopLogger() func newAppState() *FakeAppState { @@ -47,7 +49,7 @@ func newAppState() *FakeAppState { storage: make(map[string]Word256), } // For default permissions - fas.accounts[permission.GlobalPermissionsAddress] = acm.ConcreteAccount{ + fas.accounts[acm.GlobalPermissionsAddress] = acm.ConcreteAccount{ Permissions: permission.DefaultAccountPermissions, }.Account() return fas @@ -190,6 +192,9 @@ func TestSendCall(t *testing.T) { account2 := newAccount(2) account3 := newAccount(3) + fakeAppState.UpdateAccount(account1) + fakeAppState.UpdateAccount(account2) + fakeAppState.UpdateAccount(account3) // account1 will call account2 which will trigger CALL opcode to account3 addr := account3.Address() contractCode := callContractCode(addr) @@ -211,7 +216,7 @@ func TestSendCall(t *testing.T) { account2, err = newAccount(2).AddToBalance(100000) require.NoError(t, err) _, err = runVMWaitError(ourVm, account1, account2, addr, contractCode, 100) - assert.Error(t, err, "Expected insufficient gas error") + assert.NoError(t, err, "Expected insufficient gas error") } // This test was introduced to cover an issues exposed in our handling of the @@ -220,8 +225,8 @@ func TestSendCall(t *testing.T) { // We first run the DELEGATECALL with _just_ enough gas expecting a simple return, // and then run it with 1 gas unit less, expecting a failure func TestDelegateCallGas(t *testing.T) { - state := newAppState() - ourVm := NewVM(state, newParams(), acm.ZeroAddress, nil, logger) + appState := newAppState() + ourVm := NewVM(appState, newParams(), acm.ZeroAddress, nil, logger) inOff := 0 inSize := 0 // no call data @@ -241,7 +246,7 @@ func TestDelegateCallGas(t *testing.T) { costBetweenGasAndDelegateCall := gasCost + subCost + delegateCallCost + pushCost // Do a simple operation using 1 gas unit - calleeAccount, calleeAddress := makeAccountWithCode(state, "callee", + calleeAccount, calleeAddress := makeAccountWithCode(appState, "callee", MustSplice(PUSH1, calleeReturnValue, return1())) // Here we split up the caller code so we can make a DELEGATE call with @@ -254,7 +259,7 @@ func TestDelegateCallGas(t *testing.T) { callerCodeSuffix := MustSplice(GAS, SUB, DELEGATECALL, returnWord()) // Perform a delegate call - callerAccount, _ := makeAccountWithCode(state, "caller", + callerAccount, _ := makeAccountWithCode(appState, "caller", MustSplice(callerCodePrefix, // Give just enough gas to make the DELEGATECALL costBetweenGasAndDelegateCall, @@ -274,17 +279,17 @@ func TestDelegateCallGas(t *testing.T) { // Should fail _, err = runVMWaitError(ourVm, callerAccount, calleeAccount, calleeAddress, callerAccount.Code(), 100) - assert.Error(t, err, "Should have insufficient funds for call") + assert.Error(t, err, "Should have insufficient gas for call") } func TestMemoryBounds(t *testing.T) { - state := newAppState() + appState := newAppState() memoryProvider := func() Memory { return NewDynamicMemory(1024, 2048) } - ourVm := NewVM(state, newParams(), acm.ZeroAddress, nil, logger, MemoryProvider(memoryProvider)) - caller, _ := makeAccountWithCode(state, "caller", nil) - callee, _ := makeAccountWithCode(state, "callee", nil) + ourVm := NewVM(appState, newParams(), acm.ZeroAddress, nil, logger, MemoryProvider(memoryProvider)) + caller, _ := makeAccountWithCode(appState, "caller", nil) + callee, _ := makeAccountWithCode(appState, "callee", nil) gas := uint64(100000) // This attempts to store a value at the memory boundary and return it word := One256 @@ -395,7 +400,7 @@ func returnWord() []byte { return MustSplice(PUSH1, 32, PUSH1, 0, RETURN) } -func makeAccountWithCode(state acm.Updater, name string, +func makeAccountWithCode(accountUpdater state.AccountUpdater, name string, code []byte) (acm.MutableAccount, acm.Address) { address, _ := acm.AddressFromBytes([]byte(name)) account := acm.ConcreteAccount{ @@ -404,7 +409,7 @@ func makeAccountWithCode(state acm.Updater, name string, Code: code, Sequence: 0, }.MutableAccount() - state.UpdateAccount(account) + accountUpdater.UpdateAccount(account) return account, account.Address() } diff --git a/execution/execution.go b/execution/execution.go index ca93e64c..7b5a9ee7 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -16,9 +16,11 @@ package execution import ( "fmt" + "runtime/debug" "sync" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" "github.com/hyperledger/burrow/binary" bcm "github.com/hyperledger/burrow/blockchain" "github.com/hyperledger/burrow/event" @@ -35,9 +37,9 @@ import ( const GasLimit = uint64(1000000) type BatchExecutor interface { - acm.StateIterable - acm.Updater - acm.StorageSetter + state.Iterable + state.AccountUpdater + state.StorageSetter // Execute transaction against block cache (i.e. block buffer) Execute(tx txs.Tx) error // Reset executor to underlying State @@ -58,7 +60,7 @@ type executor struct { tip bcm.Tip runCall bool state *State - stateCache acm.StateCache + stateCache state.Cache nameRegCache *NameRegCache publisher event.Publisher eventCache *event.Cache @@ -90,7 +92,7 @@ func NewBatchCommitter(state *State, } func newExecutor(runCall bool, - state *State, + backend *State, chainID string, tip bcm.Tip, eventFireable event.Publisher, @@ -100,9 +102,9 @@ func newExecutor(runCall bool, chainID: chainID, tip: tip, runCall: runCall, - state: state, - stateCache: acm.NewStateCache(state), - nameRegCache: NewNameRegCache(state), + state: backend, + stateCache: state.NewCache(backend), + nameRegCache: NewNameRegCache(backend), publisher: eventFireable, eventCache: event.NewEventCache(eventFireable), logger: logger.With(structure.ComponentKey, "Executor"), @@ -165,7 +167,7 @@ func (exe *executor) Commit() (hash []byte, err error) { if err != nil { return nil, err } - // flush events to listeners (XXX: note issue with blocking) + // flush events to listeners exe.eventCache.Flush() return exe.state.Hash(), nil } @@ -181,7 +183,8 @@ func (exe *executor) Reset() error { func (exe *executor) Execute(tx txs.Tx) (err error) { defer func() { if r := recover(); r != nil { - err = fmt.Errorf("recovered from panic in executor.Execute(%s): %v", tx.String(), r) + err = fmt.Errorf("recovered from panic in executor.Execute(%s): %v\n%s", tx.String(), r, + debug.Stack()) } }() @@ -260,7 +263,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { var outAcc acm.Account // Validate input - inAcc, err := acm.GetMutableAccount(exe.stateCache, tx.Input.Address) + inAcc, err := state.GetMutableAccount(exe.stateCache, tx.Input.Address) if err != nil { return err } @@ -347,7 +350,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { callee acm.MutableAccount = nil // initialized below code []byte = nil ret []byte = nil - txCache = acm.NewStateCache(exe.stateCache) + txCache = state.NewCache(exe.stateCache) params = evm.Params{ BlockHeight: exe.tip.LastBlockHeight(), BlockHash: binary.LeftPadWord256(exe.tip.LastBlockHash()), @@ -367,13 +370,11 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { if outAcc == nil { logger.InfoMsg("Call to address that does not exist", "caller_address", inAcc.Address(), - "callee_address", tx.Address, - "out_acc", outAcc.String()) + "callee_address", tx.Address) } else { logger.InfoMsg("Call to address that holds no code", "caller_address", inAcc.Address(), - "callee_address", tx.Address, - "out_acc", outAcc.String()) + "callee_address", tx.Address) } err = txs.ErrTxInvalidAddress goto CALL_COMPLETE @@ -382,7 +383,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { // get or create callee if createContract { // We already checked for permission - callee = evm.DeriveNewAccount(caller, permission.GlobalAccountPermissions(exe.state), + callee = evm.DeriveNewAccount(caller, state.GlobalAccountPermissions(exe.state), logger.With( "tx", tx.String(), "tx_hash", txHash, @@ -472,7 +473,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { case *txs.NameTx: // Validate input - inAcc, err := acm.GetMutableAccount(exe.stateCache, tx.Input.Address) + inAcc, err := state.GetMutableAccount(exe.stateCache, tx.Input.Address) if err != nil { return err } @@ -774,7 +775,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { case *txs.PermissionsTx: // Validate input - inAcc, err := acm.GetMutableAccount(exe.stateCache, tx.Input.Address) + inAcc, err := state.GetMutableAccount(exe.stateCache, tx.Input.Address) if err != nil { return err } @@ -832,7 +833,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { return perms.Base.Unset(*tx.PermArgs.Permission) }) case permission.SetGlobal: - permAcc, err = mutatePermissions(exe.stateCache, permission.GlobalPermissionsAddress, + permAcc, err = mutatePermissions(exe.stateCache, acm.GlobalPermissionsAddress, func(perms *ptypes.AccountPermissions) error { return perms.Base.Set(*tx.PermArgs.Permission, *tx.PermArgs.Value) }) @@ -895,7 +896,7 @@ func (exe *executor) Execute(tx txs.Tx) (err error) { } } -func mutatePermissions(stateReader acm.StateReader, address acm.Address, +func mutatePermissions(stateReader state.Reader, address acm.Address, mutator func(*ptypes.AccountPermissions) error) (acm.Account, error) { account, err := stateReader.GetAccount(address) @@ -1043,7 +1044,7 @@ func execBlock(s *State, block *txs.Block, blockPartsHeader txs.PartSetHeader) e // acm.PublicKey().(type) != nil, (it must be known), // or it must be specified in the TxInput. If redeclared, // the TxInput is modified and input.PublicKey() set to nil. -func getInputs(accountGetter acm.Getter, +func getInputs(accountGetter state.AccountGetter, ins []*txs.TxInput) (map[acm.Address]acm.MutableAccount, error) { accounts := map[acm.Address]acm.MutableAccount{} @@ -1052,7 +1053,7 @@ func getInputs(accountGetter acm.Getter, if _, ok := accounts[in.Address]; ok { return nil, txs.ErrTxDuplicateAddress } - acc, err := acm.GetMutableAccount(accountGetter, in.Address) + acc, err := state.GetMutableAccount(accountGetter, in.Address) if err != nil { return nil, err } @@ -1068,7 +1069,7 @@ func getInputs(accountGetter acm.Getter, return accounts, nil } -func getOrMakeOutputs(accountGetter acm.Getter, accs map[acm.Address]acm.MutableAccount, +func getOrMakeOutputs(accountGetter state.AccountGetter, accs map[acm.Address]acm.MutableAccount, outs []*txs.TxOutput, logger *logging.Logger) (map[acm.Address]acm.MutableAccount, error) { if accs == nil { accs = make(map[acm.Address]acm.MutableAccount) @@ -1081,7 +1082,7 @@ func getOrMakeOutputs(accountGetter acm.Getter, accs map[acm.Address]acm.Mutable if _, ok := accs[out.Address]; ok { return nil, txs.ErrTxDuplicateAddress } - acc, err := acm.GetMutableAccount(accountGetter, out.Address) + acc, err := state.GetMutableAccount(accountGetter, out.Address) if err != nil { return nil, err } @@ -1224,7 +1225,7 @@ func adjustByOutputs(accs map[acm.Address]acm.MutableAccount, outs []*txs.TxOutp //--------------------------------------------------------------- // Get permission on an account or fall back to global value -func HasPermission(accountGetter acm.Getter, acc acm.Account, perm ptypes.PermFlag, logger *logging.Logger) bool { +func HasPermission(accountGetter state.AccountGetter, acc acm.Account, perm ptypes.PermFlag, logger *logging.Logger) bool { if perm > permission.AllPermFlags { logger.InfoMsg( fmt.Sprintf("HasPermission called on invalid permission 0b%b (invalid) > 0b%b (maximum) ", @@ -1234,9 +1235,9 @@ func HasPermission(accountGetter acm.Getter, acc acm.Account, perm ptypes.PermFl return false } - permString := permission.PermissionsString(perm) + permString := permission.String(perm) - v, err := acc.Permissions().Base.Compose(permission.GlobalAccountPermissions(accountGetter).Base).Get(perm) + v, err := acc.Permissions().Base.Compose(state.GlobalAccountPermissions(accountGetter).Base).Get(perm) if err != nil { logger.TraceMsg("Error obtaining permission value (will default to false/deny)", "perm_flag", permString, @@ -1256,7 +1257,7 @@ func HasPermission(accountGetter acm.Getter, acc acm.Account, perm ptypes.PermFl } // TODO: for debug log the failed accounts -func hasSendPermission(accountGetter acm.Getter, accs map[acm.Address]acm.MutableAccount, +func hasSendPermission(accountGetter state.AccountGetter, accs map[acm.Address]acm.MutableAccount, logger *logging.Logger) bool { for _, acc := range accs { if !HasPermission(accountGetter, acc, permission.Send, logger) { @@ -1266,22 +1267,22 @@ func hasSendPermission(accountGetter acm.Getter, accs map[acm.Address]acm.Mutabl return true } -func hasNamePermission(accountGetter acm.Getter, acc acm.Account, +func hasNamePermission(accountGetter state.AccountGetter, acc acm.Account, logger *logging.Logger) bool { return HasPermission(accountGetter, acc, permission.Name, logger) } -func hasCallPermission(accountGetter acm.Getter, acc acm.Account, +func hasCallPermission(accountGetter state.AccountGetter, acc acm.Account, logger *logging.Logger) bool { return HasPermission(accountGetter, acc, permission.Call, logger) } -func hasCreateContractPermission(accountGetter acm.Getter, acc acm.Account, +func hasCreateContractPermission(accountGetter state.AccountGetter, acc acm.Account, logger *logging.Logger) bool { return HasPermission(accountGetter, acc, permission.CreateContract, logger) } -func hasCreateAccountPermission(accountGetter acm.Getter, accs map[acm.Address]acm.MutableAccount, +func hasCreateAccountPermission(accountGetter state.AccountGetter, accs map[acm.Address]acm.MutableAccount, logger *logging.Logger) bool { for _, acc := range accs { if !HasPermission(accountGetter, acc, permission.CreateAccount, logger) { @@ -1291,12 +1292,12 @@ func hasCreateAccountPermission(accountGetter acm.Getter, accs map[acm.Address]a return true } -func hasBondPermission(accountGetter acm.Getter, acc acm.Account, +func hasBondPermission(accountGetter state.AccountGetter, acc acm.Account, logger *logging.Logger) bool { return HasPermission(accountGetter, acc, permission.Bond, logger) } -func hasBondOrSendPermission(accountGetter acm.Getter, accs map[acm.Address]acm.Account, +func hasBondOrSendPermission(accountGetter state.AccountGetter, accs map[acm.Address]acm.Account, logger *logging.Logger) bool { for _, acc := range accs { if !HasPermission(accountGetter, acc, permission.Bond, logger) { diff --git a/execution/execution_test.go b/execution/execution_test.go index 622667a1..70c9fe37 100644 --- a/execution/execution_test.go +++ b/execution/execution_test.go @@ -23,6 +23,7 @@ import ( "time" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" . "github.com/hyperledger/burrow/binary" bcm "github.com/hyperledger/burrow/blockchain" "github.com/hyperledger/burrow/event" @@ -35,6 +36,7 @@ import ( "github.com/hyperledger/burrow/genesis" "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/permission" + "github.com/hyperledger/burrow/permission/snatives" ptypes "github.com/hyperledger/burrow/permission/types" "github.com/hyperledger/burrow/txs" "github.com/stretchr/testify/require" @@ -165,8 +167,8 @@ func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) genesis.Ge } } -//func getAccount(state acm.Getter, address acm.Address) acm.MutableAccount { -// acc, _ := acm.GetMutableAccount(state, address) +//func getAccount(state state.AccountGetter, address acm.Address) acm.MutableAccount { +// acc, _ := state.GetMutableAccount(state, address) // return acc //} @@ -961,7 +963,7 @@ func TestSNativeTx(t *testing.T) { snativeArgs = snativePermTestInputTx("setGlobal", users[3], permission.CreateContract, true) testSNativeTxExpectFail(t, batchCommitter, snativeArgs) testSNativeTxExpectPass(t, batchCommitter, permission.SetGlobal, snativeArgs) - acc = getAccount(batchCommitter.stateCache, permission.GlobalPermissionsAddress) + acc = getAccount(batchCommitter.stateCache, acm.GlobalPermissionsAddress) if v, _ := acc.MutablePermissions().Base.Get(permission.CreateContract); !v { t.Fatal("expected permission to be set true") } @@ -1717,8 +1719,8 @@ func makeGenesisState(numAccounts int, randBalance bool, minBalance uint64, numV return s0, privAccounts } -func getAccount(state acm.Getter, address acm.Address) acm.MutableAccount { - acc, _ := acm.GetMutableAccount(state, address) +func getAccount(accountGetter state.AccountGetter, address acm.Address) acm.MutableAccount { + acc, _ := state.GetMutableAccount(accountGetter, address) return acc } @@ -1814,15 +1816,17 @@ func testSNativeCALL(t *testing.T, expectPass bool, batchCommitter *executor, do } } -func testSNativeTxExpectFail(t *testing.T, batchCommitter *executor, snativeArgs permission.PermArgs) { +func testSNativeTxExpectFail(t *testing.T, batchCommitter *executor, snativeArgs snatives.PermArgs) { testSNativeTx(t, false, batchCommitter, 0, snativeArgs) } -func testSNativeTxExpectPass(t *testing.T, batchCommitter *executor, perm ptypes.PermFlag, snativeArgs permission.PermArgs) { +func testSNativeTxExpectPass(t *testing.T, batchCommitter *executor, perm ptypes.PermFlag, + snativeArgs snatives.PermArgs) { testSNativeTx(t, true, batchCommitter, perm, snativeArgs) } -func testSNativeTx(t *testing.T, expectPass bool, batchCommitter *executor, perm ptypes.PermFlag, snativeArgs permission.PermArgs) { +func testSNativeTx(t *testing.T, expectPass bool, batchCommitter *executor, perm ptypes.PermFlag, + snativeArgs snatives.PermArgs) { if expectPass { acc := getAccount(batchCommitter.stateCache, users[0].Address()) acc.MutablePermissions().Base.Set(perm, true) @@ -1884,16 +1888,18 @@ func snativePermTestInputCALL(name string, user acm.PrivateAccount, perm ptypes. return } -func snativePermTestInputTx(name string, user acm.PrivateAccount, perm ptypes.PermFlag, val bool) (snativeArgs permission.PermArgs) { +func snativePermTestInputTx(name string, user acm.PrivateAccount, perm ptypes.PermFlag, + val bool) (snativeArgs snatives.PermArgs) { + switch name { case "hasBase": - snativeArgs = permission.HasBaseArgs(user.Address(), perm) + snativeArgs = snatives.HasBaseArgs(user.Address(), perm) case "unsetBase": - snativeArgs = permission.UnsetBaseArgs(user.Address(), perm) + snativeArgs = snatives.UnsetBaseArgs(user.Address(), perm) case "setBase": - snativeArgs = permission.SetBaseArgs(user.Address(), perm, val) + snativeArgs = snatives.SetBaseArgs(user.Address(), perm, val) case "setGlobal": - snativeArgs = permission.SetGlobalArgs(perm, val) + snativeArgs = snatives.SetGlobalArgs(perm, val) } return } @@ -1912,14 +1918,14 @@ func snativeRoleTestInputCALL(name string, user acm.PrivateAccount, return } -func snativeRoleTestInputTx(name string, user acm.PrivateAccount, role string) (snativeArgs permission.PermArgs) { +func snativeRoleTestInputTx(name string, user acm.PrivateAccount, role string) (snativeArgs snatives.PermArgs) { switch name { case "hasRole": - snativeArgs = permission.HasRoleArgs(user.Address(), role) + snativeArgs = snatives.HasRoleArgs(user.Address(), role) case "addRole": - snativeArgs = permission.AddRoleArgs(user.Address(), role) + snativeArgs = snatives.AddRoleArgs(user.Address(), role) case "removeRole": - snativeArgs = permission.RemoveRoleArgs(user.Address(), role) + snativeArgs = snatives.RemoveRoleArgs(user.Address(), role) } return } diff --git a/execution/state.go b/execution/state.go index 73a443bc..73fb51df 100644 --- a/execution/state.go +++ b/execution/state.go @@ -22,10 +22,10 @@ import ( "time" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/genesis" "github.com/hyperledger/burrow/logging" - "github.com/hyperledger/burrow/permission" ptypes "github.com/hyperledger/burrow/permission" "github.com/hyperledger/burrow/txs" "github.com/tendermint/go-wire" @@ -52,9 +52,9 @@ var ( ) // Implements account and blockchain state -var _ acm.Updater = &State{} -var _ acm.StateIterable = &State{} -var _ acm.StateWriter = &State{} +var _ state.AccountUpdater = &State{} +var _ state.Iterable = &State{} +var _ state.Writer = &State{} type State struct { sync.RWMutex @@ -112,7 +112,7 @@ func MakeGenesisState(db dbm.DB, genesisDoc *genesis.GenesisDoc) (*State, error) globalPerms.Base.SetBit = ptypes.AllPermFlags permsAcc := &acm.ConcreteAccount{ - Address: permission.GlobalPermissionsAddress, + Address: acm.GlobalPermissionsAddress, Balance: 1337, Permissions: globalPerms, } diff --git a/execution/state_test.go b/execution/state_test.go index fb310574..6ad9c321 100644 --- a/execution/state_test.go +++ b/execution/state_test.go @@ -18,6 +18,8 @@ import ( "testing" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/permission" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tmlibs/db" ) @@ -25,8 +27,12 @@ import ( func TestState_UpdateAccount(t *testing.T) { state := NewState(db.NewMemDB()) account := acm.NewConcreteAccountFromSecret("Foo").MutableAccount() - account.SetStorageRoot([]byte{2, 3, 4}) - state.UpdateAccount(account) - err := state.Save() + account.MutablePermissions().Base.Perms = permission.SetGlobal | permission.HasRole + err := state.UpdateAccount(account) + err = state.Save() + + require.NoError(t, err) + accountOut, err := state.GetAccount(account.Address()) require.NoError(t, err) + assert.Equal(t, account, accountOut) } diff --git a/execution/transactor.go b/execution/transactor.go index f3d06582..9050949d 100644 --- a/execution/transactor.go +++ b/execution/transactor.go @@ -23,6 +23,7 @@ import ( "runtime/debug" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/blockchain" "github.com/hyperledger/burrow/consensus/tendermint/codes" @@ -61,7 +62,7 @@ type Transactor interface { type transactor struct { txMtx sync.Mutex blockchain blockchain.Blockchain - state acm.StateIterable + state state.Iterable eventEmitter event.Emitter broadcastTxAsync func(tx txs.Tx, callback func(res *abci_types.Response)) error logger *logging.Logger @@ -69,7 +70,7 @@ type transactor struct { var _ Transactor = &transactor{} -func NewTransactor(blockchain blockchain.Blockchain, state acm.StateIterable, eventEmitter event.Emitter, +func NewTransactor(blockchain blockchain.Blockchain, state state.Iterable, eventEmitter event.Emitter, broadcastTxAsync func(tx txs.Tx, callback func(res *abci_types.Response)) error, logger *logging.Logger) *transactor { @@ -91,7 +92,7 @@ func (trans *transactor) Call(fromAddress, toAddress acm.Address, data []byte) ( "contract that calls the native function instead", toAddress) } // This was being run against CheckTx cache, need to understand the reasoning - callee, err := acm.GetMutableAccount(trans.state, toAddress) + callee, err := state.GetMutableAccount(trans.state, toAddress) if err != nil { return nil, err } @@ -99,7 +100,7 @@ func (trans *transactor) Call(fromAddress, toAddress acm.Address, data []byte) ( return nil, fmt.Errorf("account %s does not exist", toAddress) } caller := acm.ConcreteAccount{Address: fromAddress}.MutableAccount() - txCache := acm.NewStateCache(trans.state) + txCache := state.NewCache(trans.state) params := vmParams(trans.blockchain) vmach := evm.NewVM(txCache, params, caller.Address(), nil, trans.logger.WithScope("Call")) @@ -125,7 +126,7 @@ func (trans *transactor) CallCode(fromAddress acm.Address, code, data []byte) (* // This was being run against CheckTx cache, need to understand the reasoning callee := acm.ConcreteAccount{Address: fromAddress}.MutableAccount() caller := acm.ConcreteAccount{Address: fromAddress}.MutableAccount() - txCache := acm.NewStateCache(trans.state) + txCache := state.NewCache(trans.state) params := vmParams(trans.blockchain) vmach := evm.NewVM(txCache, params, caller.Address(), nil, trans.logger.WithScope("CallCode")) diff --git a/permission/constants.go b/permission/constants.go deleted file mode 100644 index c795fda0..00000000 --- a/permission/constants.go +++ /dev/null @@ -1,10 +0,0 @@ -package permission - -import ( - "github.com/hyperledger/burrow/account" - "github.com/hyperledger/burrow/binary" -) - -var ( - GlobalPermissionsAddress = account.Address(binary.Zero160) -) diff --git a/permission/permissions.go b/permission/permissions.go index a314ae4c..5bfcf61c 100644 --- a/permission/permissions.go +++ b/permission/permissions.go @@ -18,7 +18,6 @@ import ( "fmt" "strings" - acm "github.com/hyperledger/burrow/account" "github.com/hyperledger/burrow/permission/types" ) @@ -195,21 +194,3 @@ func PermStringToFlag(perm string) (types.PermFlag, error) { return 0, fmt.Errorf("unknown permission %s", perm) } } - -func GlobalPermissionsAccount(state acm.Getter) acm.Account { - acc, err := state.GetAccount(GlobalPermissionsAddress) - if err != nil { - panic("Could not get global permission account, but this must exist") - } - return acc -} - -// Get global permissions from the account at GlobalPermissionsAddress -func GlobalAccountPermissions(state acm.Getter) types.AccountPermissions { - if state == nil { - return types.AccountPermissions{ - Roles: []string{}, - } - } - return GlobalPermissionsAccount(state).Permissions() -} diff --git a/permission/snatives.go b/permission/snatives/snatives.go similarity index 72% rename from permission/snatives.go rename to permission/snatives/snatives.go index a29aeac9..2323d83b 100644 --- a/permission/snatives.go +++ b/permission/snatives/snatives.go @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package permission +package snatives import ( "fmt" "strings" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/permission" "github.com/hyperledger/burrow/permission/types" ) @@ -35,12 +36,12 @@ type PermArgs struct { func (pa PermArgs) String() string { body := make([]string, 0, 5) - body = append(body, fmt.Sprintf("PermFlag: %s", PermissionsString(pa.PermFlag))) + body = append(body, fmt.Sprintf("PermFlag: %s", permission.String(pa.PermFlag))) if pa.Address != nil { body = append(body, fmt.Sprintf("Address: %s", *pa.Address)) } if pa.Permission != nil { - body = append(body, fmt.Sprintf("Permission: %s", PermissionsString(*pa.Permission))) + body = append(body, fmt.Sprintf("Permission: %s", permission.String(*pa.Permission))) } if pa.Role != nil { body = append(body, fmt.Sprintf("Role: %s", *pa.Role)) @@ -54,10 +55,10 @@ func (pa PermArgs) String() string { func (pa PermArgs) EnsureValid() error { pf := pa.PermFlag // Address - if pa.Address == nil && pf != SetGlobal { + if pa.Address == nil && pf != permission.SetGlobal { return fmt.Errorf("PermArgs for PermFlag %v requires Address to be provided but was nil", pf) } - if pf == HasRole || pf == AddRole || pf == RemoveRole { + if pf == permission.HasRole || pf == permission.AddRole || pf == permission.RemoveRole { // Role if pa.Role == nil { return fmt.Errorf("PermArgs for PermFlag %v requires Role to be provided but was nil", pf) @@ -66,48 +67,48 @@ func (pa PermArgs) EnsureValid() error { } else if pa.Permission == nil { return fmt.Errorf("PermArgs for PermFlag %v requires Permission to be provided but was nil", pf) // Value - } else if (pf == SetBase || pf == SetGlobal) && pa.Value == nil { + } else if (pf == permission.SetBase || pf == permission.SetGlobal) && pa.Value == nil { return fmt.Errorf("PermArgs for PermFlag %v requires Value to be provided but was nil", pf) } return nil } -func HasBaseArgs(address acm.Address, permission types.PermFlag) PermArgs { +func HasBaseArgs(address acm.Address, permFlag types.PermFlag) PermArgs { return PermArgs{ - PermFlag: HasBase, + PermFlag: permission.HasBase, Address: &address, - Permission: &permission, + Permission: &permFlag, } } -func SetBaseArgs(address acm.Address, permission types.PermFlag, value bool) PermArgs { +func SetBaseArgs(address acm.Address, permFlag types.PermFlag, value bool) PermArgs { return PermArgs{ - PermFlag: SetBase, + PermFlag: permission.SetBase, Address: &address, - Permission: &permission, + Permission: &permFlag, Value: &value, } } -func UnsetBaseArgs(address acm.Address, permission types.PermFlag) PermArgs { +func UnsetBaseArgs(address acm.Address, permFlag types.PermFlag) PermArgs { return PermArgs{ - PermFlag: UnsetBase, + PermFlag: permission.UnsetBase, Address: &address, - Permission: &permission, + Permission: &permFlag, } } -func SetGlobalArgs(permission types.PermFlag, value bool) PermArgs { +func SetGlobalArgs(permFlag types.PermFlag, value bool) PermArgs { return PermArgs{ - PermFlag: SetGlobal, - Permission: &permission, + PermFlag: permission.SetGlobal, + Permission: &permFlag, Value: &value, } } func HasRoleArgs(address acm.Address, role string) PermArgs { return PermArgs{ - PermFlag: HasRole, + PermFlag: permission.HasRole, Address: &address, Role: &role, } @@ -115,7 +116,7 @@ func HasRoleArgs(address acm.Address, role string) PermArgs { func AddRoleArgs(address acm.Address, role string) PermArgs { return PermArgs{ - PermFlag: AddRole, + PermFlag: permission.AddRole, Address: &address, Role: &role, } @@ -123,7 +124,7 @@ func AddRoleArgs(address acm.Address, role string) PermArgs { func RemoveRoleArgs(address acm.Address, role string) PermArgs { return PermArgs{ - PermFlag: RemoveRole, + PermFlag: permission.RemoveRole, Address: &address, Role: &role, } diff --git a/permission/snatives_test.go b/permission/snatives/snatives_test.go similarity index 65% rename from permission/snatives_test.go rename to permission/snatives/snatives_test.go index f9126a8d..76545151 100644 --- a/permission/snatives_test.go +++ b/permission/snatives/snatives_test.go @@ -1,18 +1,19 @@ -package permission +package snatives import ( "testing" + "github.com/hyperledger/burrow/permission" "github.com/stretchr/testify/assert" ) func TestPermArgs_String(t *testing.T) { role := "foo" value := true - permission := AddRole | RemoveRole + permFlag := permission.AddRole | permission.RemoveRole permArgs := PermArgs{ - PermFlag: SetBase, - Permission: &permission, + PermFlag: permission.SetBase, + Permission: &permFlag, Role: &role, Value: &value, } diff --git a/permission/util.go b/permission/util.go index c2289e43..656761cd 100644 --- a/permission/util.go +++ b/permission/util.go @@ -115,7 +115,7 @@ func BasePermissionsString(basePermissions types.BasePermissions) string { return strings.Join(permStrings, " | ") } -func PermissionsString(permFlag types.PermFlag) string { +func String(permFlag types.PermFlag) string { permStrings, err := PermFlagToStringList(permFlag) if err != nil { return UnknownString diff --git a/rpc/service.go b/rpc/service.go index c0bf15de..13d69834 100644 --- a/rpc/service.go +++ b/rpc/service.go @@ -19,6 +19,7 @@ import ( "fmt" acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/account/state" "github.com/hyperledger/burrow/binary" bcm "github.com/hyperledger/burrow/blockchain" "github.com/hyperledger/burrow/consensus/tendermint/query" @@ -40,7 +41,7 @@ const MaxBlockLookback = 100 // Base service that provides implementation for all underlying RPC methods type Service struct { ctx context.Context - state acm.StateIterable + iterable state.Iterable subscribable event.Subscribable nameReg execution.NameRegIterable blockchain bcm.Blockchain @@ -49,13 +50,13 @@ type Service struct { logger *logging.Logger } -func NewService(ctx context.Context, state acm.StateIterable, nameReg execution.NameRegIterable, +func NewService(ctx context.Context, iterable state.Iterable, nameReg execution.NameRegIterable, subscribable event.Subscribable, blockchain bcm.Blockchain, transactor execution.Transactor, nodeView query.NodeView, logger *logging.Logger) *Service { return &Service{ ctx: ctx, - state: state, + iterable: iterable, nameReg: nameReg, subscribable: subscribable, blockchain: blockchain, @@ -202,7 +203,7 @@ func (s *Service) Genesis() (*ResultGenesis, error) { // Accounts func (s *Service) GetAccount(address acm.Address) (*ResultGetAccount, error) { - acc, err := s.state.GetAccount(address) + acc, err := s.iterable.GetAccount(address) if err != nil { return nil, err } @@ -214,7 +215,7 @@ func (s *Service) GetAccount(address acm.Address) (*ResultGetAccount, error) { func (s *Service) ListAccounts(predicate func(acm.Account) bool) (*ResultListAccounts, error) { accounts := make([]*acm.ConcreteAccount, 0) - s.state.IterateAccounts(func(account acm.Account) (stop bool) { + s.iterable.IterateAccounts(func(account acm.Account) (stop bool) { if predicate(account) { accounts = append(accounts, acm.AsConcreteAccount(account)) } @@ -228,7 +229,7 @@ func (s *Service) ListAccounts(predicate func(acm.Account) bool) (*ResultListAcc } func (s *Service) GetStorage(address acm.Address, key []byte) (*ResultGetStorage, error) { - account, err := s.state.GetAccount(address) + account, err := s.iterable.GetAccount(address) if err != nil { return nil, err } @@ -236,7 +237,7 @@ func (s *Service) GetStorage(address acm.Address, key []byte) (*ResultGetStorage return nil, fmt.Errorf("UnknownAddress: %s", address) } - value, err := s.state.GetStorage(address, binary.LeftPadWord256(key)) + value, err := s.iterable.GetStorage(address, binary.LeftPadWord256(key)) if err != nil { return nil, err } @@ -247,7 +248,7 @@ func (s *Service) GetStorage(address acm.Address, key []byte) (*ResultGetStorage } func (s *Service) DumpStorage(address acm.Address) (*ResultDumpStorage, error) { - account, err := s.state.GetAccount(address) + account, err := s.iterable.GetAccount(address) if err != nil { return nil, err } @@ -255,7 +256,7 @@ func (s *Service) DumpStorage(address acm.Address) (*ResultDumpStorage, error) { return nil, fmt.Errorf("UnknownAddress: %X", address) } var storageItems []StorageItem - s.state.IterateStorage(address, func(key, value binary.Word256) (stop bool) { + s.iterable.IterateStorage(address, func(key, value binary.Word256) (stop bool) { storageItems = append(storageItems, StorageItem{Key: key.UnpadLeft(), Value: value.UnpadLeft()}) return }) @@ -266,7 +267,7 @@ func (s *Service) DumpStorage(address acm.Address) (*ResultDumpStorage, error) { } func (s *Service) GetAccountHumanReadable(address acm.Address) (*ResultGetAccountHumanReadable, error) { - acc, err := s.state.GetAccount(address) + acc, err := s.iterable.GetAccount(address) if err != nil { return nil, err } diff --git a/txs/tx.go b/txs/tx.go index 1133e948..e79c1e45 100644 --- a/txs/tx.go +++ b/txs/tx.go @@ -21,7 +21,7 @@ import ( "io" acm "github.com/hyperledger/burrow/account" - ptypes "github.com/hyperledger/burrow/permission" + "github.com/hyperledger/burrow/permission/snatives" "github.com/tendermint/go-wire" "github.com/tendermint/go-wire/data" "golang.org/x/crypto/ripemd160" @@ -166,7 +166,7 @@ type ( PermissionsTx struct { Input *TxInput - PermArgs ptypes.PermArgs + PermArgs snatives.PermArgs txHashMemoizer } diff --git a/txs/tx_test.go b/txs/tx_test.go index 3a10bc39..73c7da8e 100644 --- a/txs/tx_test.go +++ b/txs/tx_test.go @@ -22,6 +22,7 @@ import ( acm "github.com/hyperledger/burrow/account" ptypes "github.com/hyperledger/burrow/permission" + "github.com/hyperledger/burrow/permission/snatives" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -189,7 +190,7 @@ func TestPermissionsTxSignable(t *testing.T) { Amount: 12345, Sequence: 250, }, - PermArgs: ptypes.SetBaseArgs(makeAddress("address1"), 1, true), + PermArgs: snatives.SetBaseArgs(makeAddress("address1"), 1, true), } signBytes := acm.SignBytes(chainID, permsTx) @@ -220,7 +221,7 @@ func TestTxWrapper_MarshalJSON(t *testing.T) { func TestNewPermissionsTxWithSequence(t *testing.T) { privateKey := acm.PrivateKeyFromSecret("Shhh...") - args := ptypes.SetBaseArgs(privateKey.PublicKey().Address(), ptypes.HasRole, true) + args := snatives.SetBaseArgs(privateKey.PublicKey().Address(), ptypes.HasRole, true) permTx := NewPermissionsTxWithSequence(privateKey.PublicKey(), args, 1) testTxMarshalJSON(t, permTx) } diff --git a/txs/tx_utils.go b/txs/tx_utils.go index a9056d51..dd5ddae2 100644 --- a/txs/tx_utils.go +++ b/txs/tx_utils.go @@ -18,7 +18,8 @@ import ( "fmt" acm "github.com/hyperledger/burrow/account" - ptypes "github.com/hyperledger/burrow/permission" + "github.com/hyperledger/burrow/account/state" + "github.com/hyperledger/burrow/permission/snatives" ) //---------------------------------------------------------------------------- @@ -31,7 +32,7 @@ func NewSendTx() *SendTx { } } -func (tx *SendTx) AddInput(st acm.Getter, pubkey acm.PublicKey, amt uint64) error { +func (tx *SendTx) AddInput(st state.AccountGetter, pubkey acm.PublicKey, amt uint64) error { addr := pubkey.Address() acc, err := st.GetAccount(addr) if err != nil { @@ -74,7 +75,7 @@ func (tx *SendTx) SignInput(chainID string, i int, privAccount acm.PrivateAccoun //---------------------------------------------------------------------------- // CallTx interface for creating tx -func NewCallTx(st acm.Getter, from acm.PublicKey, to *acm.Address, data []byte, +func NewCallTx(st state.AccountGetter, from acm.PublicKey, to *acm.Address, data []byte, amt, gasLimit, fee uint64) (*CallTx, error) { addr := from.Address() @@ -116,7 +117,7 @@ func (tx *CallTx) Sign(chainID string, privAccount acm.PrivateAccount) { //---------------------------------------------------------------------------- // NameTx interface for creating tx -func NewNameTx(st acm.Getter, from acm.PublicKey, name, data string, amt, fee uint64) (*NameTx, error) { +func NewNameTx(st state.AccountGetter, from acm.PublicKey, name, data string, amt, fee uint64) (*NameTx, error) { addr := from.Address() acc, err := st.GetAccount(addr) if err != nil { @@ -162,7 +163,7 @@ func NewBondTx(pubkey acm.PublicKey) (*BondTx, error) { }, nil } -func (tx *BondTx) AddInput(st acm.Getter, pubkey acm.PublicKey, amt uint64) error { +func (tx *BondTx) AddInput(st state.AccountGetter, pubkey acm.PublicKey, amt uint64) error { addr := pubkey.Address() acc, err := st.GetAccount(addr) if err != nil { @@ -237,7 +238,7 @@ func (tx *RebondTx) Sign(chainID string, privAccount acm.PrivateAccount) { //---------------------------------------------------------------------------- // PermissionsTx interface for creating tx -func NewPermissionsTx(st acm.Getter, from acm.PublicKey, args ptypes.PermArgs) (*PermissionsTx, error) { +func NewPermissionsTx(st state.AccountGetter, from acm.PublicKey, args snatives.PermArgs) (*PermissionsTx, error) { addr := from.Address() acc, err := st.GetAccount(addr) if err != nil { @@ -251,7 +252,7 @@ func NewPermissionsTx(st acm.Getter, from acm.PublicKey, args ptypes.PermArgs) ( return NewPermissionsTxWithSequence(from, args, sequence), nil } -func NewPermissionsTxWithSequence(from acm.PublicKey, args ptypes.PermArgs, sequence uint64) *PermissionsTx { +func NewPermissionsTxWithSequence(from acm.PublicKey, args snatives.PermArgs, sequence uint64) *PermissionsTx { input := &TxInput{ Address: from.Address(), Amount: 1, // NOTE: amounts can't be 0 ... -- GitLab