--- a/net/minecraft/world/inventory/GrindstoneMenu.java
+++ b/net/minecraft/world/inventory/GrindstoneMenu.java
@@ -20,6 +_,21 @@
 import net.minecraft.world.phys.Vec3;
 
 public class GrindstoneMenu extends AbstractContainerMenu {
+    // CraftBukkit start
+    private org.bukkit.craftbukkit.inventory.CraftInventoryView bukkitEntity = null;
+    private org.bukkit.entity.Player player;
+
+    @Override
+    public org.bukkit.craftbukkit.inventory.CraftInventoryView getBukkitView() {
+        if (this.bukkitEntity != null) {
+            return this.bukkitEntity;
+        }
+
+        org.bukkit.craftbukkit.inventory.CraftInventoryGrindstone inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryGrindstone(this.repairSlots, this.resultSlots);
+        this.bukkitEntity = new org.bukkit.craftbukkit.inventory.CraftInventoryView(this.player, inventory, this);
+        return this.bukkitEntity;
+    }
+    // CraftBukkit end
     public static final int MAX_NAME_LENGTH = 35;
     public static final int INPUT_SLOT = 0;
     public static final int ADDITIONAL_SLOT = 1;
@@ -28,14 +_,8 @@
     private static final int INV_SLOT_END = 30;
     private static final int USE_ROW_SLOT_START = 30;
     private static final int USE_ROW_SLOT_END = 39;
-    private final Container resultSlots = new ResultContainer();
-    final Container repairSlots = new SimpleContainer(2) {
-        @Override
-        public void setChanged() {
-            super.setChanged();
-            GrindstoneMenu.this.slotsChanged(this);
-        }
-    };
+    private final Container resultSlots; // Paper - Add missing InventoryHolders - move down
+    final Container repairSlots; // Paper - Add missing InventoryHolders - move down
     private final ContainerLevelAccess access;
 
     public GrindstoneMenu(int containerId, Inventory playerInventory) {
@@ -44,6 +_,22 @@
 
     public GrindstoneMenu(int containerId, Inventory playerInventory, final ContainerLevelAccess access) {
         super(MenuType.GRINDSTONE, containerId);
+        // Paper start - Add missing InventoryHolders
+        this.resultSlots = new ResultContainer(this.createBlockHolder(access)); // Paper - Add missing InventoryHolders
+        this.repairSlots = new SimpleContainer(this.createBlockHolder(access), 2) { // Paper - Add missing InventoryHolders
+            @Override
+            public void setChanged() {
+                super.setChanged();
+                GrindstoneMenu.this.slotsChanged(this);
+            }
+            // CraftBukkit start
+            @Override
+            public org.bukkit.Location getLocation() {
+                return access.getLocation();
+            }
+            // CraftBukkit end
+        };
+        // Paper end - Add missing InventoryHolders
         this.access = access;
         this.addSlot(new Slot(this.repairSlots, 0, 49, 19) {
             @Override
@@ -67,7 +_,11 @@
             public void onTake(Player player, ItemStack stack) {
                 access.execute((level, blockPos) -> {
                     if (level instanceof ServerLevel) {
-                        ExperienceOrb.award((ServerLevel)level, Vec3.atCenterOf(blockPos), this.getExperienceAmount(level));
+                        // Paper start - Fire BlockExpEvent on grindstone use
+                        org.bukkit.event.block.BlockExpEvent event = new org.bukkit.event.block.BlockExpEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, blockPos), this.getExperienceAmount(level));
+                        event.callEvent();
+                        ExperienceOrb.award((ServerLevel) level, Vec3.atCenterOf(blockPos), event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player);
+                        // Paper end - Fire BlockExpEvent on grindstone use
                     }
 
                     level.levelEvent(1042, blockPos, 0);
@@ -104,6 +_,7 @@
             }
         });
         this.addStandardInventorySlots(playerInventory, 8, 84);
+        this.player = (org.bukkit.entity.Player) playerInventory.player.getBukkitEntity(); // CraftBukkit
     }
 
     @Override
@@ -111,11 +_,13 @@
         super.slotsChanged(inventory);
         if (inventory == this.repairSlots) {
             this.createResult();
+            org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareResultEvent(this, RESULT_SLOT); // Paper - Add PrepareResultEvent
         }
     }
 
     private void createResult() {
-        this.resultSlots.setItem(0, this.computeResult(this.repairSlots.getItem(0), this.repairSlots.getItem(1)));
+        org.bukkit.craftbukkit.event.CraftEventFactory.callPrepareGrindstoneEvent(this.getBukkitView(), this.computeResult(this.repairSlots.getItem(0), this.repairSlots.getItem(1))); // CraftBukkit
+        this.sendAllDataToRemote(); // CraftBukkit - SPIGOT-6686: Always send completed inventory to stay in sync with client
         this.broadcastChanges();
     }
 
@@ -201,6 +_,7 @@
 
     @Override
     public boolean stillValid(Player player) {
+        if (!this.checkReachable) return true; // CraftBukkit
         return stillValid(this.access, player, Blocks.GRINDSTONE);
     }
 
