Skip to content
This repository has been archived by the owner on Jun 9, 2024. It is now read-only.

feat(state): Refund Plugin #114

Merged
merged 11 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions eth/core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/berachain/stargazer/lib/utils"
)

// Compile-time function assertion.
// Compile-time type assertion.
var _ vm.CanTransferFunc = canTransfer
var _ vm.TransferFunc = transfer

Expand All @@ -32,8 +32,7 @@ func canTransfer(sdb vm.GethStateDB, addr common.Address, amount *big.Int) bool
return sdb.GetBalance(addr).Cmp(amount) >= 0
}

// `transfer` subtracts amount from sender and adds amount to recipient using the `vm.GethStateDB`.
// `transfer` subtracts amount from sender and adds amount to recipient using a `vm.GethStateDB`.
func transfer(sdb vm.GethStateDB, sender, recipient common.Address, amount *big.Int) {
// We use `TransferBalance` to use the same logic as the native transfer in x/bank.
utils.MustGetAs[vm.StargazerStateDB](sdb).TransferBalance(sender, recipient, amount)
}
29 changes: 29 additions & 0 deletions eth/core/state/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (C) 2023, Berachain Foundation. All rights reserved.
// See the file LICENSE for licensing terms.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package state

import libtypes "github.com/berachain/stargazer/lib/types"

// `RefundPlugin` is a `Store` that tracks the refund counter.
type RefundPlugin interface {
// `RefundPlugin` implements `libtypes.Snapshottable`.
libtypes.Controllable[string]
// `GetRefund` returns the current value of the refund counter.
GetRefund() uint64
// `AddRefund` sets the refund counter to the given `gas`.
AddRefund(gas uint64)
// `SubRefund` subtracts the given `gas` from the refund counter.
SubRefund(gas uint64)
}
29 changes: 29 additions & 0 deletions eth/core/state/plugin/plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (C) 2023, Berachain Foundation. All rights reserved.
// See the file LICENSE for licensing terms.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package plugin

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestPlugin(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "eth/core/state/plugin")
}
80 changes: 80 additions & 0 deletions eth/core/state/plugin/refund.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (C) 2023, Berachain Foundation. All rights reserved.
// See the file LICENSE for licensing terms.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package plugin

import (
"github.com/berachain/stargazer/eth/core/state"
"github.com/berachain/stargazer/lib/ds"
"github.com/berachain/stargazer/lib/ds/stack"
)

const (
// `initCapacity` is the initial capacity of the `refund`'s snapshot stack.
initCapacity = 16
// `refundRegistryKey` is the registry key for the `refund` plugin.
refundRegistryKey = "refund"
)

// `refund` is a `Store` that tracks the refund counter.
type refund struct {
ds.Stack[uint64] // journal of historical refunds.
}

// `NewRefund` creates and returns a `refund`.
func NewRefund() state.RefundPlugin {
stack := stack.New[uint64](initCapacity)
return &refund{
Stack: stack,
}
}

// `RegistryKey` implements `libtypes.Controllable`.
func (r *refund) RegistryKey() string {
return refundRegistryKey
}

// `GetRefund` returns the current value of the refund counter.
func (r *refund) GetRefund() uint64 {
// When the refund counter is empty, the stack will return 0 by design.
return r.Peek()
}

// `AddRefund` sets the refund counter to the given `gas`.
func (r *refund) AddRefund(gas uint64) {
r.Push(r.Peek() + gas)
}

// `SubRefund` subtracts the given `gas` from the refund counter.
func (r *refund) SubRefund(gas uint64) {
r.Push(r.Peek() - gas)
}

// `Snapshot` returns the current size of the refund counter, which is used to
// revert the refund counter to a previous value.
//
// `Snapshot` implements `libtypes.Snapshottable`.
func (r *refund) Snapshot() int {
return r.Size()
}

// `RevertToSnapshot` reverts the refund counter to the value at the given `snap`.
//
// `RevertToSnapshot` implements `libtypes.Snapshottable`.
func (r *refund) RevertToSnapshot(id int) {
r.PopToSize(id)
}

// `Finalize` implements `libtypes.Controllable`.
func (r *refund) Finalize() {}
131 changes: 131 additions & 0 deletions eth/core/state/plugin/refund_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (C) 2023, Berachain Foundation. All rights reserved.
// See the file LICENSE for licensing terms.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package plugin

import (
"github.com/berachain/stargazer/eth/core/state"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Refund", func() {
var r state.RefundPlugin

BeforeEach(func() {
r = NewRefund()
})
It("should have the correct registry key", func() {
Expect(r.RegistryKey()).To(Equal("refund"))
})
When("adding a refund", func() {
BeforeEach(func() {
r.AddRefund(1)
})
It("should return the correct refund", func() {
Expect(r.GetRefund()).To(Equal(uint64(1)))
})
When("subtracting a refund", func() {
BeforeEach(func() {
r.SubRefund(1)
})
It("should return the correct refund", func() {
Expect(r.GetRefund()).To(BeZero())
})
})
})
When("pushing an element", func() {
BeforeEach(func() {
r.AddRefund(1)
})
It("should not be empty", func() {
Expect(r.Snapshot()).To(Equal(1))
})
It("should return the correct element", func() {
Expect(r.GetRefund()).To(Equal(uint64(1)))
})
When("subbing refund", func() {
BeforeEach(func() {
r.SubRefund(1)
})

It("should be empty", func() {
Expect(r.GetRefund()).To(BeZero())
})
})
When("pushing more elements and snapshotting", func() {
BeforeEach(func() {
r.AddRefund(2)
Expect(r.Snapshot()).To(Equal(2))
r.AddRefund(3)
Expect(r.Snapshot()).To(Equal(3))
})
It("should return the correct element", func() {
Expect(r.GetRefund()).To(Equal(uint64(6)))
})
When("subbing an element", func() {
BeforeEach(func() {
r.SubRefund(3)
})
It("should return the correct element", func() {
Expect(r.GetRefund()).To(Equal(uint64(3)))
})
When("subbing an element", func() {
BeforeEach(func() {
r.SubRefund(3)
})
It("should return the correct element", func() {
Expect(r.GetRefund()).To(Equal(uint64(0)))
})
When("taking a snapshot", func() {
BeforeEach(func() {
Expect(r.Snapshot()).To(Equal(5))
})

When("adding more elements", func() {
BeforeEach(func() {
r.AddRefund(1)
})
When("reverting to snapshot", func() {
BeforeEach(func() {
r.RevertToSnapshot(1)
})
It("should return the correct element", func() {
Expect(r.GetRefund()).To(Equal(uint64(1)))
})
})
})
})
})
When("finally reverting to snapshot", func() {
BeforeEach(func() {
r.RevertToSnapshot(0)
})

It("should return the correct element", func() {
Expect(r.GetRefund()).To(Equal(uint64(0)))
})
})
When("finalize", func() {
It("should not panic", func() {
Expect(func() {
r.Finalize()
}).ToNot(Panic())
})
})
})
})
})
})
4 changes: 4 additions & 0 deletions lib/ds/stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func New[T any](capacity int) ds.Stack[T] {

// `Peek` implements `Stack`.
func (s *stack[T]) Peek() T {
if s.size == 0 {
var t T
return t
}
return s.buf[s.size-1]
}

Expand Down
49 changes: 47 additions & 2 deletions lib/types/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,54 @@ type Cloneable[T any] interface {
// `Snapshottable` is an interface that defines methods for snapshotting and reverting
// a logical unit of data.
type Snapshottable interface {
// `Snapshot` returns an identifier for the current revision of the data.
Snapshot() int

// `RevertToSnapshot` reverts the data to a previous version
RevertToSnapshot(int)
}

// `Snapshot` returns an identifier for the current revision of the data.
Snapshot() int
// `Registrable` is an interface that all objects that can be registered in a
// `Registry` must implement.
type Registrable[K comparable] interface {
// `RegistryKey` returns the key that will be used to register the object.
RegistryKey() K
}

// `Registry` is an interface that all objects that can be used as a registry
// must implement.
type Registry[K comparable, T Registrable[K]] interface {
// Get return an item using its ID. It returns nil if the ID does not exist.
Get(K) T

// Register adds an item to the registry, indexed on the item's `RegistryKey`.
Register(T) error

// Remove removes an item from the registry.
Remove(K)

// Exists returns true if the item exists in the registry.
Exists(K) bool

// Iterate returns an iterable map of the registry.
Iterate() map[K]T
}

// `Controllable` defines a type which can be controlled.
type Controllable[K comparable] interface {
Snapshottable
Registrable[K]
Finalizeable
}

// `Controller` is an interface for controller types.
type Controller[K comparable, T Controllable[K]] interface {
Snapshottable
Registry[K, T]
Finalizeable
}

// `Finalizeable` is an interface that defines a `Finalize` method.
type Finalizeable interface {
Finalize()
}