Resolve merge conflict

This commit is contained in:
Ian Davis 2023-01-19 14:10:03 +00:00
commit f2ab452eb8
9 changed files with 146 additions and 78 deletions

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b5061018c806100206000396000f3fe6080604052600436106100345760003560e01c806361bc221a146100395780636466414b146100645780638ada066e14610080575b600080fd5b34801561004557600080fd5b5061004e6100ab565b60405161005b91906100dd565b60405180910390f35b61007e60048036038101906100799190610129565b6100b1565b005b34801561008c57600080fd5b506100956100bb565b6040516100a291906100dd565b60405180910390f35b60005481565b8060008190555050565b60008054905090565b6000819050919050565b6100d7816100c4565b82525050565b60006020820190506100f260008301846100ce565b92915050565b600080fd5b610106816100c4565b811461011157600080fd5b50565b600081359050610123816100fd565b92915050565b60006020828403121561013f5761013e6100f8565b5b600061014d84828501610114565b9150509291505056fea2646970667358221220cf4567855a30be48cde5cdbff495bdaa4052e2c4540678b97284af53a4e5dbd164736f6c63430008110033

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract DelegatecallActor {
uint public counter;
function getCounter() public view returns (uint){
return counter;
}
function setVars(uint _counter) public payable {
counter = _counter;
}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b50610477806100206000396000f3fe6080604052600436106100345760003560e01c806361bc221a146100395780638ada066e14610064578063d1e0f3081461008f575b600080fd5b34801561004557600080fd5b5061004e6100bf565b60405161005b919061022c565b60405180910390f35b34801561007057600080fd5b506100796100c5565b604051610086919061022c565b60405180910390f35b6100a960048036038101906100a491906102d6565b6100ce565b6040516100b6919061022c565b60405180910390f35b60005481565b60008054905090565b6000808373ffffffffffffffffffffffffffffffffffffffff16836040516024016100f9919061022c565b6040516020818303038152906040527f6466414b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516101839190610387565b600060405180830381855af49150503d80600081146101be576040519150601f19603f3d011682016040523d82523d6000602084013e6101c3565b606091505b5050905080610207576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101fe90610421565b60405180910390fd5b60005491505092915050565b6000819050919050565b61022681610213565b82525050565b6000602082019050610241600083018461021d565b92915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102778261024c565b9050919050565b6102878161026c565b811461029257600080fd5b50565b6000813590506102a48161027e565b92915050565b6102b381610213565b81146102be57600080fd5b50565b6000813590506102d0816102aa565b92915050565b600080604083850312156102ed576102ec610247565b5b60006102fb85828601610295565b925050602061030c858286016102c1565b9150509250929050565b600081519050919050565b600081905092915050565b60005b8381101561034a57808201518184015260208101905061032f565b60008484015250505050565b600061036182610316565b61036b8185610321565b935061037b81856020860161032c565b80840191505092915050565b60006103938284610356565b915081905092915050565b600082825260208201905092915050565b7f4572726f72206d6573736167653a2044656c656761746563616c6c206661696c60008201527f6564000000000000000000000000000000000000000000000000000000000000602082015250565b600061040b60228361039e565b9150610416826103af565b604082019050919050565b6000602082019050818103600083015261043a816103fe565b905091905056fea26469706673582212203663909b8221e9b87047be99420c00339af1430c085260df209b909ed8e0f05164736f6c63430008110033

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract DelegatecallStorage {
uint public counter;
function getCounter() public view returns (uint){
return counter;
}
function setVars(address _contract, uint _counter) public payable returns (uint){
(bool success, ) = _contract.delegatecall(
abi.encodeWithSignature("setVars(uint256)", _counter)
);
require(success, 'Error message: Delegatecall failed');
return counter;
}
}

View File

@ -1 +1 @@
608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061051c806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610193919061047e565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104b2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b925082820261046081610337565b915082820484148315176104775761047661040d565b5b5092915050565b600061048982610337565b915061049483610337565b92508282039050818111156104ac576104ab61040d565b5b92915050565b60006104bd82610337565b91506104c883610337565b92508282019050808211156104e0576104df61040d565b5b9291505056fea26469706673582212205ede41ff9072784ccc19ac18de0781558d305a8139361fa85dc51a8614e47d8c64736f6c63430008110033 608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061051c806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610193919061047e565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104b2565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b925082820261046081610337565b915082820484148315176104775761047661040d565b5b5092915050565b600061048982610337565b915061049483610337565b92508282039050818111156104ac576104ab61040d565b5b92915050565b60006104bd82610337565b91506104c883610337565b92508282019050808211156104e0576104df61040d565b5b9291505056fea2646970667358221220050cdcfbe2911d041d2e6c355dbb6a0ca8ca70b500865bf33d9a2e5f4ac5a4e164736f6c63430008110033

6
itests/contracts/compile.sh Executable file
View File

@ -0,0 +1,6 @@
#use the solc compiler https://docs.soliditylang.org/en/v0.8.17/installing-solidity.html
# to compile all of the .sol files to their corresponding evm binary files stored as .hex
# solc outputs to stdout a format that we just want to grab the last line of and then remove the trailing newline on that line
find -type f -name \*.sol -print0 |
xargs -0 -I{} bash -c 'solc --bin {} |tail -n1 | tr -d "\n" > $(echo {} | sed -e s/.sol$/.hex/)'

View File

@ -1300,18 +1300,6 @@ type msgInTipset struct {
reverted bool reverted bool
} }
func installContract(ctx context.Context, t *testing.T, client *kit.TestFullNode, filename string) (address.Address, address.Address) {
sender, err := client.WalletDefaultAddress(ctx)
require.NoError(t, err)
result := client.EVM().DeployContractFile(ctx, sender, filename)
actorAddr, err := address.NewIDAddress(result.ActorID)
require.NoError(t, err)
return sender, actorAddr
}
func getContractEthAddress(ctx context.Context, t *testing.T, client *kit.TestFullNode, addr address.Address) ethtypes.EthAddress { func getContractEthAddress(ctx context.Context, t *testing.T, client *kit.TestFullNode, addr address.Address) ethtypes.EthAddress {
head, err := client.ChainHead(ctx) head, err := client.ChainHead(ctx)
require.NoError(t, err) require.NoError(t, err)
@ -1449,7 +1437,7 @@ func invokeLogFourData(t *testing.T, client *kit.TestFullNode, iterations int) (
ctx, cancel := context.WithTimeout(context.Background(), time.Minute) ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel() defer cancel()
fromAddr, idAddr := installContract(ctx, t, client, EventsContract.Filename) fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, EventsContract.Filename)
invocations := make([]Invocation, iterations) invocations := make([]Invocation, iterations)
for i := range invocations { for i := range invocations {
@ -1469,8 +1457,8 @@ func invokeLogFourData(t *testing.T, client *kit.TestFullNode, iterations int) (
} }
func invokeEventMatrix(ctx context.Context, t *testing.T, client *kit.TestFullNode) (ethtypes.EthAddress, ethtypes.EthAddress, map[ethtypes.EthHash]msgInTipset) { func invokeEventMatrix(ctx context.Context, t *testing.T, client *kit.TestFullNode) (ethtypes.EthAddress, ethtypes.EthAddress, map[ethtypes.EthHash]msgInTipset) {
sender1, contract1 := installContract(ctx, t, client, EventMatrixContract.Filename) sender1, contract1 := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename)
sender2, contract2 := installContract(ctx, t, client, EventMatrixContract.Filename) sender2, contract2 := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename)
invocations := []Invocation{ invocations := []Invocation{
// log EventZeroData() // log EventZeroData()

View File

@ -1,67 +1,64 @@
package itests package itests
import ( import (
"bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"os"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
cbg "github.com/whyrusleeping/cbor-gen"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
builtintypes "github.com/filecoin-project/go-state-types/builtin" builtintypes "github.com/filecoin-project/go-state-types/builtin"
"github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/go-state-types/manifest"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/itests/kit"
) )
// TestFEVMBasic does a basic fevm contract installation and invocation // convert a simple byte array into input data which is a left padded 32 byte array
func TestFEVMBasic(t *testing.T) { func inputDataFromArray(input []byte) []byte {
// TODO the contract installation and invocation can be lifted into utility methods inputData := make([]byte, 32)
// He who writes the second test, shall do that. copy(inputData[32-len(input):], input[:])
kit.QuietMiningLogs() return inputData
}
// convert a "from" address into input data which is a left padded 32 byte array
func inputDataFromFrom(ctx context.Context, t *testing.T, client *kit.TestFullNode, from address.Address) []byte {
fromId, err := client.StateLookupID(ctx, from, types.EmptyTSK)
require.NoError(t, err)
senderEthAddr, err := ethtypes.EthAddressFromFilecoinAddress(fromId)
require.NoError(t, err)
inputData := make([]byte, 32)
copy(inputData[32-len(senderEthAddr):], senderEthAddr[:])
return inputData
}
func setupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *kit.TestFullNode) {
kit.QuietMiningLogs()
blockTime := 100 * time.Millisecond blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime) ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute) ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
return ctx, cancel, client
}
// TestFEVMBasic does a basic fevm contract installation and invocation
func TestFEVMBasic(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t)
defer cancel() defer cancel()
filename := "contracts/SimpleCoin.hex"
// install contract // install contract
contractHex, err := os.ReadFile("contracts/SimpleCoin.hex") fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
require.NoError(t, err)
contract, err := hex.DecodeString(string(contractHex))
require.NoError(t, err)
fromAddr, err := client.WalletDefaultAddress(ctx)
require.NoError(t, err)
result := client.EVM().DeployContract(ctx, fromAddr, contract)
idAddr, err := address.NewIDAddress(result.ActorID)
require.NoError(t, err)
t.Logf("actor ID address is %s", idAddr)
// invoke the contract with owner // invoke the contract with owner
{ {
entryPoint, err := hex.DecodeString("f8b2cb4f") inputData := inputDataFromFrom(ctx, t, client, fromAddr)
require.NoError(t, err) result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData)
inputData, err := hex.DecodeString("000000000000000000000000ff00000000000000000000000000000000000064")
require.NoError(t, err)
wait := client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
require.True(t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed")
result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return)))
require.NoError(t, err)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000002710") expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000002710")
require.NoError(t, err) require.NoError(t, err)
@ -70,34 +67,19 @@ func TestFEVMBasic(t *testing.T) {
// invoke the contract with non owner // invoke the contract with non owner
{ {
entryPoint, err := hex.DecodeString("f8b2cb4f") inputData := inputDataFromFrom(ctx, t, client, fromAddr)
require.NoError(t, err) inputData[31]++ // change the pub address to one that has 0 balance by incrementing the last byte of the address
result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData)
inputData, err := hex.DecodeString("000000000000000000000000ff00000000000000000000000000000000000065")
require.NoError(t, err)
wait := client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
require.True(t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed")
result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return)))
require.NoError(t, err)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, result, expectedResult) require.Equal(t, result, expectedResult)
} }
} }
// TestFEVMETH0 tests that the ETH0 actor is in genesis // TestFEVMETH0 tests that the ETH0 actor is in genesis
func TestFEVMETH0(t *testing.T) { func TestFEVMETH0(t *testing.T) {
kit.QuietMiningLogs() ctx, cancel, client := setupFEVMTest(t)
blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel() defer cancel()
eth0id, err := address.NewIDAddress(1001) eth0id, err := address.NewIDAddress(1001)
@ -112,3 +94,41 @@ func TestFEVMETH0(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, *act.Address, eth0Addr) require.Equal(t, *act.Address, eth0Addr)
} }
// TestFEVMDelegateCall deploys two contracts and makes a delegate call transaction
func TestFEVMDelegateCall(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t)
defer cancel()
//install contract Actor
filenameActor := "contracts/DelegatecallActor.hex"
fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
//install contract Storage
filenameStorage := "contracts/DelegatecallStorage.hex"
fromAddrStorage, storageAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
require.Equal(t, fromAddr, fromAddrStorage)
//call Contract Storage which makes a delegatecall to contract Actor
//this contract call sets the "counter" variable to 7, from default value 0
inputDataContract := inputDataFromFrom(ctx, t, client, actorAddr)
inputDataValue := inputDataFromArray([]byte{7})
inputData := append(inputDataContract, inputDataValue...)
//verify that the returned value of the call to setvars is 7
result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "setVars(address,uint256)", inputData)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000007")
require.NoError(t, err)
require.Equal(t, result, expectedResult)
//test the value is 7 via calling the getter
result = client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "getCounter()", []byte{})
require.Equal(t, result, expectedResult)
//test the value is 0 via calling the getter on the Actor contract
result = client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "getCounter()", []byte{})
expectedResultActor, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)
require.Equal(t, result, expectedResultActor)
}

View File

@ -79,19 +79,24 @@ func (e *EVM) DeployContract(ctx context.Context, sender address.Address, byteco
return result return result
} }
func (e *EVM) DeployContractFile(ctx context.Context, sender address.Address, filename string) eam.CreateReturn { func (e *EVM) DeployContractFromFilename(ctx context.Context, binFilename string) (address.Address, address.Address) {
require := require.New(e.t) contractHex, err := os.ReadFile(binFilename)
require.NoError(e.t, err)
contractHex, err := os.ReadFile(filename)
require.NoError(err)
// strip any trailing newlines from the file // strip any trailing newlines from the file
contractHex = bytes.TrimRight(contractHex, "\n") contractHex = bytes.TrimRight(contractHex, "\n")
bytecode, err := hex.DecodeString(string(contractHex)) contract, err := hex.DecodeString(string(contractHex))
require.NoError(err) require.NoError(e.t, err)
return e.DeployContract(ctx, sender, bytecode) fromAddr, err := e.WalletDefaultAddress(ctx)
require.NoError(e.t, err)
result := e.DeployContract(ctx, fromAddr, contract)
idAddr, err := address.NewIDAddress(result.ActorID)
require.NoError(e.t, err)
return fromAddr, idAddr
} }
func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target address.Address, selector []byte, inputData []byte) *api.MsgLookup { func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target address.Address, selector []byte, inputData []byte) *api.MsgLookup {
@ -229,6 +234,23 @@ func (e *EVM) ComputeContractAddress(deployer ethtypes.EthAddress, nonce uint64)
return *(*ethtypes.EthAddress)(hasher.Sum(nil)[12:]) return *(*ethtypes.EthAddress)(hasher.Sum(nil)[12:])
} }
func (e *EVM) InvokeContractByFuncName(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte) []byte {
entryPoint := CalcFuncSignature(funcSignature)
wait := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
require.True(e.t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed")
result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return)))
require.NoError(e.t, err)
return result
}
// function signatures are the first 4 bytes of the hash of the function name and types
func CalcFuncSignature(funcName string) []byte {
hasher := sha3.NewLegacyKeccak256()
hasher.Write([]byte(funcName))
hash := hasher.Sum(nil)
return hash[:4]
}
// TODO: cleanup and put somewhere reusable. // TODO: cleanup and put somewhere reusable.
type apiIpldStore struct { type apiIpldStore struct {
ctx context.Context ctx context.Context