forked from cerc-io/plugeth
all: expose block number information to statedb (#27753)
* core/state: clean up * all: add block number infomration to statedb * core, trie: rename blockNumber to block
This commit is contained in:
+2
-2
@@ -173,11 +173,11 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
|
||||
}
|
||||
obj := newObject(s, addr, &data)
|
||||
if !conf.SkipCode {
|
||||
account.Code = obj.Code(s.db)
|
||||
account.Code = obj.Code()
|
||||
}
|
||||
if !conf.SkipStorage {
|
||||
account.Storage = make(map[common.Hash]string)
|
||||
tr, err := obj.getTrie(s.db)
|
||||
tr, err := obj.getTrie()
|
||||
if err != nil {
|
||||
log.Error("Failed to load storage trie", "err", err)
|
||||
continue
|
||||
|
||||
@@ -366,7 +366,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi
|
||||
return false, nil, err
|
||||
}
|
||||
if nodes != nil {
|
||||
tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
|
||||
tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
|
||||
tdb.Commit(root, false)
|
||||
}
|
||||
resolver = func(owner common.Hash, path []byte, hash common.Hash) []byte {
|
||||
|
||||
@@ -203,7 +203,7 @@ func (t *testHelper) Commit() common.Hash {
|
||||
if nodes != nil {
|
||||
t.nodes.Merge(nodes)
|
||||
}
|
||||
t.triedb.Update(root, types.EmptyRootHash, t.nodes, nil)
|
||||
t.triedb.Update(root, types.EmptyRootHash, 0, t.nodes, nil)
|
||||
t.triedb.Commit(root, false)
|
||||
return root
|
||||
}
|
||||
|
||||
+19
-19
@@ -137,7 +137,7 @@ func (s *stateObject) touch() {
|
||||
// getTrie returns the associated storage trie. The trie will be opened
|
||||
// if it's not loaded previously. An error will be returned if trie can't
|
||||
// be loaded.
|
||||
func (s *stateObject) getTrie(db Database) (Trie, error) {
|
||||
func (s *stateObject) getTrie() (Trie, error) {
|
||||
if s.trie == nil {
|
||||
// Try fetching from prefetcher first
|
||||
if s.data.Root != types.EmptyRootHash && s.db.prefetcher != nil {
|
||||
@@ -145,7 +145,7 @@ func (s *stateObject) getTrie(db Database) (Trie, error) {
|
||||
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
|
||||
}
|
||||
if s.trie == nil {
|
||||
tr, err := db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root)
|
||||
tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -156,18 +156,18 @@ func (s *stateObject) getTrie(db Database) (Trie, error) {
|
||||
}
|
||||
|
||||
// GetState retrieves a value from the account storage trie.
|
||||
func (s *stateObject) GetState(db Database, key common.Hash) common.Hash {
|
||||
func (s *stateObject) GetState(key common.Hash) common.Hash {
|
||||
// If we have a dirty value for this state entry, return it
|
||||
value, dirty := s.dirtyStorage[key]
|
||||
if dirty {
|
||||
return value
|
||||
}
|
||||
// Otherwise return the entry's original value
|
||||
return s.GetCommittedState(db, key)
|
||||
return s.GetCommittedState(key)
|
||||
}
|
||||
|
||||
// GetCommittedState retrieves a value from the committed account storage trie.
|
||||
func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
|
||||
func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
||||
// If we have a pending write or clean cached, return that
|
||||
if value, pending := s.pendingStorage[key]; pending {
|
||||
return value
|
||||
@@ -207,7 +207,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
|
||||
// If the snapshot is unavailable or reading from it fails, load from the database.
|
||||
if s.db.snap == nil || err != nil {
|
||||
start := time.Now()
|
||||
tr, err := s.getTrie(db)
|
||||
tr, err := s.getTrie()
|
||||
if err != nil {
|
||||
s.db.setError(err)
|
||||
return common.Hash{}
|
||||
@@ -227,9 +227,9 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
|
||||
}
|
||||
|
||||
// SetState updates a value in account storage.
|
||||
func (s *stateObject) SetState(db Database, key, value common.Hash) {
|
||||
func (s *stateObject) SetState(key, value common.Hash) {
|
||||
// If the new value is the same as old, don't set
|
||||
prev := s.GetState(db, key)
|
||||
prev := s.GetState(key)
|
||||
if prev == value {
|
||||
return
|
||||
}
|
||||
@@ -267,7 +267,7 @@ func (s *stateObject) finalise(prefetch bool) {
|
||||
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||
// It will return nil if the trie has not been loaded and no changes have been
|
||||
// made. An error will be returned if the trie can't be loaded/updated correctly.
|
||||
func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
||||
func (s *stateObject) updateTrie() (Trie, error) {
|
||||
// Make sure all dirty slots are finalized into the pending storage area
|
||||
s.finalise(false) // Don't prefetch anymore, pull directly if need be
|
||||
if len(s.pendingStorage) == 0 {
|
||||
@@ -283,7 +283,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
||||
origin map[common.Hash][]byte
|
||||
hasher = s.db.hasher
|
||||
)
|
||||
tr, err := s.getTrie(db)
|
||||
tr, err := s.getTrie()
|
||||
if err != nil {
|
||||
s.db.setError(err)
|
||||
return nil, err
|
||||
@@ -357,8 +357,8 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) {
|
||||
|
||||
// UpdateRoot sets the trie root to the current root hash of. An error
|
||||
// will be returned if trie root hash is not computed correctly.
|
||||
func (s *stateObject) updateRoot(db Database) {
|
||||
tr, err := s.updateTrie(db)
|
||||
func (s *stateObject) updateRoot() {
|
||||
tr, err := s.updateTrie()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -374,8 +374,8 @@ func (s *stateObject) updateRoot(db Database) {
|
||||
}
|
||||
|
||||
// commit returns the changes made in storage trie and updates the account data.
|
||||
func (s *stateObject) commit(db Database) (*trienode.NodeSet, error) {
|
||||
tr, err := s.updateTrie(db)
|
||||
func (s *stateObject) commit() (*trienode.NodeSet, error) {
|
||||
tr, err := s.updateTrie()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -465,14 +465,14 @@ func (s *stateObject) Address() common.Address {
|
||||
}
|
||||
|
||||
// Code returns the contract code associated with this object, if any.
|
||||
func (s *stateObject) Code(db Database) []byte {
|
||||
func (s *stateObject) Code() []byte {
|
||||
if s.code != nil {
|
||||
return s.code
|
||||
}
|
||||
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
|
||||
return nil
|
||||
}
|
||||
code, err := db.ContractCode(s.address, common.BytesToHash(s.CodeHash()))
|
||||
code, err := s.db.db.ContractCode(s.address, common.BytesToHash(s.CodeHash()))
|
||||
if err != nil {
|
||||
s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
|
||||
}
|
||||
@@ -483,14 +483,14 @@ func (s *stateObject) Code(db Database) []byte {
|
||||
// CodeSize returns the size of the contract code associated with this object,
|
||||
// or zero if none. This method is an almost mirror of Code, but uses a cache
|
||||
// inside the database to avoid loading codes seen recently.
|
||||
func (s *stateObject) CodeSize(db Database) int {
|
||||
func (s *stateObject) CodeSize() int {
|
||||
if s.code != nil {
|
||||
return len(s.code)
|
||||
}
|
||||
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
|
||||
return 0
|
||||
}
|
||||
size, err := db.ContractCodeSize(s.address, common.BytesToHash(s.CodeHash()))
|
||||
size, err := s.db.db.ContractCodeSize(s.address, common.BytesToHash(s.CodeHash()))
|
||||
if err != nil {
|
||||
s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err))
|
||||
}
|
||||
@@ -498,7 +498,7 @@ func (s *stateObject) CodeSize(db Database) int {
|
||||
}
|
||||
|
||||
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||
prevcode := s.Code(s.db.db)
|
||||
prevcode := s.Code()
|
||||
s.db.journal.append(codeChange{
|
||||
account: &s.address,
|
||||
prevhash: s.CodeHash(),
|
||||
|
||||
@@ -58,7 +58,7 @@ func TestDump(t *testing.T) {
|
||||
// write some of them to the trie
|
||||
s.state.updateStateObject(obj1)
|
||||
s.state.updateStateObject(obj2)
|
||||
root, _ := s.state.Commit(false)
|
||||
root, _ := s.state.Commit(0, false)
|
||||
|
||||
// check that DumpToCollector contains the state objects that are in trie
|
||||
s.state, _ = New(root, tdb, nil)
|
||||
@@ -114,7 +114,7 @@ func TestIterativeDump(t *testing.T) {
|
||||
// write some of them to the trie
|
||||
s.state.updateStateObject(obj1)
|
||||
s.state.updateStateObject(obj2)
|
||||
root, _ := s.state.Commit(false)
|
||||
root, _ := s.state.Commit(0, false)
|
||||
s.state, _ = New(root, tdb, nil)
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
@@ -140,7 +140,7 @@ func TestNull(t *testing.T) {
|
||||
var value common.Hash
|
||||
|
||||
s.state.SetState(address, common.Hash{}, value)
|
||||
s.state.Commit(false)
|
||||
s.state.Commit(0, false)
|
||||
|
||||
if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
|
||||
t.Errorf("expected empty current value, got %x", value)
|
||||
@@ -212,7 +212,7 @@ func TestSnapshot2(t *testing.T) {
|
||||
so0.deleted = false
|
||||
state.setStateObject(so0)
|
||||
|
||||
root, _ := state.Commit(false)
|
||||
root, _ := state.Commit(0, false)
|
||||
state, _ = New(root, state.db, state.snaps)
|
||||
|
||||
// and one with deleted == true
|
||||
@@ -234,8 +234,8 @@ func TestSnapshot2(t *testing.T) {
|
||||
|
||||
so0Restored := state.getStateObject(stateobjaddr0)
|
||||
// Update lazily-loaded values before comparing.
|
||||
so0Restored.GetState(state.db, storageaddr)
|
||||
so0Restored.Code(state.db)
|
||||
so0Restored.GetState(storageaddr)
|
||||
so0Restored.Code()
|
||||
// non-deleted is equal (restored)
|
||||
compareStateObjects(so0Restored, so0, t)
|
||||
|
||||
|
||||
+16
-13
@@ -309,7 +309,7 @@ func (s *StateDB) TxIndex() int {
|
||||
func (s *StateDB) GetCode(addr common.Address) []byte {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.Code(s.db)
|
||||
return stateObject.Code()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -317,7 +317,7 @@ func (s *StateDB) GetCode(addr common.Address) []byte {
|
||||
func (s *StateDB) GetCodeSize(addr common.Address) int {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.CodeSize(s.db)
|
||||
return stateObject.CodeSize()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -334,7 +334,7 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
||||
func (s *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.GetState(s.db, hash)
|
||||
return stateObject.GetState(hash)
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -372,7 +372,7 @@ func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte,
|
||||
func (s *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
|
||||
stateObject := s.getStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.GetCommittedState(s.db, hash)
|
||||
return stateObject.GetCommittedState(hash)
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -391,10 +391,10 @@ func (s *StateDB) StorageTrie(addr common.Address) (Trie, error) {
|
||||
return nil, nil
|
||||
}
|
||||
cpy := stateObject.deepCopy(s)
|
||||
if _, err := cpy.updateTrie(s.db); err != nil {
|
||||
if _, err := cpy.updateTrie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cpy.getTrie(s.db)
|
||||
return cpy.getTrie()
|
||||
}
|
||||
|
||||
func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
|
||||
@@ -449,7 +449,7 @@ func (s *StateDB) SetCode(addr common.Address, code []byte) {
|
||||
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
|
||||
stateObject := s.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SetState(s.db, key, value)
|
||||
stateObject.SetState(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
|
||||
}
|
||||
stateObject := s.GetOrNewStateObject(addr)
|
||||
for k, v := range storage {
|
||||
stateObject.SetState(s.db, k, v)
|
||||
stateObject.SetState(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,7 +724,7 @@ func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.
|
||||
if so == nil {
|
||||
return nil
|
||||
}
|
||||
tr, err := so.getTrie(s.db)
|
||||
tr, err := so.getTrie()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -968,7 +968,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||
// to pull useful data from disk.
|
||||
for addr := range s.stateObjectsPending {
|
||||
if obj := s.stateObjects[addr]; !obj.deleted {
|
||||
obj.updateRoot(s.db)
|
||||
obj.updateRoot()
|
||||
}
|
||||
}
|
||||
// Now we're about to start to write changes to the trie. The trie is so far
|
||||
@@ -1155,7 +1155,10 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.H
|
||||
// trie, storage tries) will no longer be functional. A new state instance
|
||||
// must be created with new root and updated database for accessing post-
|
||||
// commit states.
|
||||
func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||
//
|
||||
// The associated block number of the state transition is also provided
|
||||
// for more chain context.
|
||||
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, error) {
|
||||
// Short circuit in case any database failure occurred earlier.
|
||||
if s.dbErr != nil {
|
||||
return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
|
||||
@@ -1190,7 +1193,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||
obj.dirtyCode = false
|
||||
}
|
||||
// Write any storage changes in the state object to its storage trie
|
||||
set, err := obj.commit(s.db)
|
||||
set, err := obj.commit()
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
@@ -1276,7 +1279,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||
Storages: s.storagesOrigin,
|
||||
Incomplete: incomplete,
|
||||
}
|
||||
if err := s.db.TrieDB().Update(root, origin, nodes, set); err != nil {
|
||||
if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
s.originalRoot = root
|
||||
|
||||
@@ -206,7 +206,7 @@ func (test *stateTest) run() bool {
|
||||
} else {
|
||||
state.IntermediateRoot(true) // call intermediateRoot at the transaction boundary
|
||||
}
|
||||
nroot, err := state.Commit(true) // call commit at the block boundary
|
||||
nroot, err := state.Commit(0, true) // call commit at the block boundary
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
+11
-11
@@ -106,7 +106,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
}
|
||||
|
||||
// Commit and cross check the databases.
|
||||
transRoot, err := transState.Commit(false)
|
||||
transRoot, err := transState.Commit(0, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit transition state: %v", err)
|
||||
}
|
||||
@@ -114,7 +114,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
t.Errorf("can not commit trie %v to persistent database", transRoot.Hex())
|
||||
}
|
||||
|
||||
finalRoot, err := finalState.Commit(false)
|
||||
finalRoot, err := finalState.Commit(0, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit final state: %v", err)
|
||||
}
|
||||
@@ -487,7 +487,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
func TestTouchDelete(t *testing.T) {
|
||||
s := newStateEnv()
|
||||
s.state.GetOrNewStateObject(common.Address{})
|
||||
root, _ := s.state.Commit(false)
|
||||
root, _ := s.state.Commit(0, false)
|
||||
s.state, _ = New(root, s.state.db, s.state.snaps)
|
||||
|
||||
snapshot := s.state.Snapshot()
|
||||
@@ -575,7 +575,7 @@ func TestCopyCommitCopy(t *testing.T) {
|
||||
t.Fatalf("second copy committed storage slot mismatch: have %x, want %x", val, sval)
|
||||
}
|
||||
// Commit state, ensure states can be loaded from disk
|
||||
root, _ := state.Commit(false)
|
||||
root, _ := state.Commit(0, false)
|
||||
state, _ = New(root, tdb, nil)
|
||||
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
|
||||
t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42)
|
||||
@@ -689,7 +689,7 @@ func TestCommitCopy(t *testing.T) {
|
||||
t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{})
|
||||
}
|
||||
// Copy the committed state database, the copied one is not functional.
|
||||
state.Commit(true)
|
||||
state.Commit(0, true)
|
||||
copied := state.Copy()
|
||||
if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Fatalf("unexpected balance: have %v", balance)
|
||||
@@ -723,7 +723,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||
addr := common.BytesToAddress([]byte("so"))
|
||||
state.SetBalance(addr, big.NewInt(1))
|
||||
|
||||
root, _ := state.Commit(false)
|
||||
root, _ := state.Commit(0, false)
|
||||
state, _ = New(root, state.db, state.snaps)
|
||||
|
||||
// Simulate self-destructing in one transaction, then create-reverting in another
|
||||
@@ -735,7 +735,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||
state.RevertToSnapshot(id)
|
||||
|
||||
// Commit the entire state and make sure we don't crash and have the correct state
|
||||
root, _ = state.Commit(true)
|
||||
root, _ = state.Commit(0, true)
|
||||
state, _ = New(root, state.db, state.snaps)
|
||||
|
||||
if state.getStateObject(addr) != nil {
|
||||
@@ -759,7 +759,7 @@ func TestMissingTrieNodes(t *testing.T) {
|
||||
a2 := common.BytesToAddress([]byte("another"))
|
||||
state.SetBalance(a2, big.NewInt(100))
|
||||
state.SetCode(a2, []byte{1, 2, 4})
|
||||
root, _ = state.Commit(false)
|
||||
root, _ = state.Commit(0, false)
|
||||
t.Logf("root: %x", root)
|
||||
// force-flush
|
||||
state.Database().TrieDB().Cap(0)
|
||||
@@ -783,7 +783,7 @@ func TestMissingTrieNodes(t *testing.T) {
|
||||
}
|
||||
// Modify the state
|
||||
state.SetBalance(addr, big.NewInt(2))
|
||||
root, err := state.Commit(false)
|
||||
root, err := state.Commit(0, false)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got root :%x", root)
|
||||
}
|
||||
@@ -978,7 +978,7 @@ func TestFlushOrderDataLoss(t *testing.T) {
|
||||
state.SetState(common.Address{a}, common.Hash{a, s}, common.Hash{a, s})
|
||||
}
|
||||
}
|
||||
root, err := state.Commit(false)
|
||||
root, err := state.Commit(0, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to commit state trie: %v", err)
|
||||
}
|
||||
@@ -1057,7 +1057,7 @@ func TestResetObject(t *testing.T) {
|
||||
state.CreateAccount(addr)
|
||||
state.SetBalance(addr, big.NewInt(2))
|
||||
state.SetState(addr, slotB, common.BytesToHash([]byte{0x2}))
|
||||
root, _ := state.Commit(true)
|
||||
root, _ := state.Commit(0, true)
|
||||
|
||||
// Ensure the original account is wiped properly
|
||||
snap := snaps.Snapshot(root)
|
||||
|
||||
@@ -64,13 +64,13 @@ func makeTestState() (ethdb.Database, Database, common.Hash, []*testAccount) {
|
||||
if i%5 == 0 {
|
||||
for j := byte(0); j < 5; j++ {
|
||||
hash := crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j})
|
||||
obj.SetState(sdb, hash, hash)
|
||||
obj.SetState(hash, hash)
|
||||
}
|
||||
}
|
||||
state.updateStateObject(obj)
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
root, _ := state.Commit(false)
|
||||
root, _ := state.Commit(0, false)
|
||||
|
||||
// Return the generated state
|
||||
return db, sdb, root, accounts
|
||||
|
||||
Reference in New Issue
Block a user