Skip to content

Instantly share code, notes, and snippets.

@ARR4N
Created March 11, 2024 16:31
Show Gist options
  • Save ARR4N/f6f7b9f29477f0759e85aba12974c360 to your computer and use it in GitHub Desktop.
Save ARR4N/f6f7b9f29477f0759e85aba12974c360 to your computer and use it in GitHub Desktop.
Solution builder for https://www.curta.wtf/golf/2
// The factorial binary builds solutions for the 2nd Curta gas-golfing course.
package main
import (
"fmt"
"math/bits"
"github.com/ethereum/go-ethereum/common"
. "github.com/solidifylabs/specops" //lint:ignore ST1001 SpecOps DSL is designed to be dot-imported
"github.com/solidifylabs/specops/specopscli"
"github.com/solidifylabs/specops/stack"
"github.com/solidifylabs/specops/types"
)
func code() Code {
// ----------------------------------------
// ========== START EDITING HERE ==========
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Cloned from https://github.com/solidifylabs/specops/tree/main/getting-started
const (
_ = Inverted(DUP1) + iota // Result
Input
)
factorial := Code{
PUSH(1), // Result
// The selector is irrelevant so just load the input word.
Fn(CALLDATALOAD, PUSH(4)),
Fn(JUMPI,
PUSH("revert"),
Fn(GT, Input, PUSH(57)),
),
stack.ExpectDepth(2),
Fn(JUMP,
Fn(SUB,
PUSH("return"),
Fn(SHL, PUSH(2) /* top of stack = Input */),
),
),
stack.ExpectDepth(1),
}
for i := 0; i < 56; i++ { // note NOT 57
var mul types.Bytecoder
switch m := uint64(57 - i); {
case m&(m-1) == 0:
mul = Fn(SHL, PUSH(bits.Len64(m)-1))
default:
mul = Fn(MUL, PUSH(m))
}
factorial = append(
factorial,
// The actual JUMPDEST label is irrelevant, but the compiler insists
// that they must be distinct.
JUMPDEST(fmt.Sprintf("%d", i)), stack.SetDepth(1),
mul,
)
}
// If we'd bounded the loop above by 57 instead of 56 then we'd have a no-op
// multiplication by 1 here, for 8 gas (PUSH + MUL). Removing it entirely
// would require adjusting the earlier JUMP, negating any benefits, so we
// can instead replace it with 3 extra JUMPDESTs for 3 gas.
for i := 0; i < 4; i++ {
factorial = append(factorial,
JUMPDEST(fmt.Sprintf("a%d", i)), stack.SetDepth(1),
)
}
return append(
factorial,
JUMPDEST("return"), stack.SetDepth(1),
Fn(MSTORE, PUSH0),
Fn(RETURN, PUSH0, PUSH(32)),
JUMPDEST("revert"), stack.SetDepth(0),
Fn(REVERT, PUSH0, PUSH0),
)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// ========== STOP EDITING HERE ===========
// ----------------------------------------
}
func main() {
specopscli.Run(code())
}
// Stop unused imports being removed.
var (
_ = stack.ExpectDepth(0)
_ = common.Address{}
)
package main
import (
"fmt"
"testing"
"github.com/holiman/uint256"
)
func TestFactorial(t *testing.T) {
want := uint256.NewInt(1)
c := code()
for i := uint64(0); i < 18; i++ {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
bigI := uint256.NewInt(i)
in := bigI.Bytes32()
if i != 0 {
want.Mul(want, bigI)
}
gotBuf, err := c.Run(append([]byte{0xd0, 0x97, 0xbc, 0x0d}, in[:]...))
if err != nil {
t.Fatal(err)
}
got := new(uint256.Int).SetBytes(gotBuf)
if !got.Eq(want) {
t.Errorf("%d! got %d; want %d", i, got, want)
}
})
}
}
module github.com/solidifylabs/specops
go 1.20
require (
github.com/ethereum/go-ethereum v1.13.14
github.com/gdamore/tcell/v2 v2.7.1
github.com/google/go-cmp v0.5.9
github.com/holiman/uint256 v1.2.4
github.com/rivo/tview v0.0.0-20240225120200-5605142ca62e
github.com/spf13/cobra v1.5.0
golang.org/x/sync v0.5.0
)
require (
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
github.com/gdamore/encoding v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/supranational/blst v0.3.11 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment