Skip to content

Commit

Permalink
Upload smart contract code (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vizualni authored Jun 23, 2022
1 parent 1641b97 commit 46949b1
Show file tree
Hide file tree
Showing 11 changed files with 346 additions and 92 deletions.
6 changes: 6 additions & 0 deletions chain/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ const (
ErrSignatureDoesNotMatchItsRespectiveSigner = whoops.String("signature does not match its respective signer")
ErrTooLittleOrTooManySignaturesProvided = whoops.String("too many or too little signatures provided")
ErrProcessorDoesNotSupportThisQueue = whoops.Errorf("processor does not support queue: %s")

ErrNotFound = whoops.String("not found")

EnrichedChainID whoops.Field[string] = "chainID"
EnrichedID whoops.Field[uint64] = "id"
EnrichedItemType whoops.Field[string] = "type"
)
111 changes: 64 additions & 47 deletions chain/evm/compass.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package evm

import (
"bytes"
"context"
goerrors "errors"
"fmt"
"math/big"

etherum "github.com/ethereum/go-ethereum"
Expand All @@ -15,6 +18,7 @@ import (
"github.com/palomachain/sparrow/errors"
"github.com/palomachain/sparrow/types/paloma/x/evm/types"
"github.com/palomachain/sparrow/util/slice"
log "github.com/sirupsen/logrus"
"github.com/vizualni/whoops"
)

Expand All @@ -30,6 +34,7 @@ const (
type evmClienter interface {
FilterLogs(ctx context.Context, fq etherum.FilterQuery, currBlockHeight *big.Int, fn func(logs []ethtypes.Log) bool) (bool, error)
ExecuteSmartContract(ctx context.Context, contractAbi abi.ABI, addr common.Address, method string, arguments []any) (*etherumtypes.Transaction, error)
DeployContract(ctx context.Context, contractAbi abi.ABI, bytecode, constructorInput []byte) (contractAddr common.Address, tx *ethtypes.Transaction, err error)
}

type compass struct {
Expand Down Expand Up @@ -90,9 +95,13 @@ func (t compass) updateValset(
currentValset, err := t.paloma.QueryGetEVMValsetByID(ctx, valsetID, t.ChainID)
whoops.Assert(err)

if currentValset == nil {
whoops.Assert(fmt.Errorf("oh no"))
}

consensusReached := isConsensusReached(currentValset, origMessage)
if !consensusReached {
return
whoops.Assert(ErrNoConsensus)
}

_, err = t.callCompass(ctx, "update_valset", []any{
Expand All @@ -107,12 +116,11 @@ func (t compass) updateValset(

func (t compass) submitLogicCall(
ctx context.Context,
messageID uint64,
msg *types.SubmitLogicCall,
origMessage chain.MessageWithSignatures,
) error {
return whoops.Try(func() {
executed, err := t.isArbitraryCallAlreadyExecuted(ctx, messageID)
executed, err := t.isArbitraryCallAlreadyExecuted(ctx, origMessage.ID)
whoops.Assert(err)
if executed {
return
Expand All @@ -126,7 +134,7 @@ func (t compass) submitLogicCall(

consensusReached := isConsensusReached(valset, origMessage)
if !consensusReached {
return
whoops.Assert(ErrNoConsensus)
}

con := buildConsensus(ctx, valset, origMessage.Signatures)
Expand All @@ -143,41 +151,32 @@ func (t compass) submitLogicCall(
})
}

// func (t compass) uploadSmartContract(
// ctx context.Context,
// messageID uint64,
// turnstoneID []byte,
// msg *types.UploadSmartContract,
// signatures []chain.ValidatorSignature,
// ) error {
// return whoops.Try(func() {
// executed, err := t.isArbitraryCallAlreadyExecuted(ctx, messageID)
// whoops.Assert(err)
// if executed {
// return
// }

// valsetID, err := t.findLastValsetMessageID(ctx)
// whoops.Assert(err)

// snapshot, err := t.Client.paloma.GetSnapshotByID(ctx, valsetID)
// whoops.Assert(err)

// bind.DeployContract()

// con := t.buildConsensus(ctx, snapshot, signatures)

// _, err = t.callSmartContract(ctx, "submit_logic_call", []any{
// con,
// common.HexToAddress(msg.GetHexContractAddress()),
// msg.GetPayload(),
// msg.GetDeadline(),
// })
// whoops.Assert(err)

// return
// })
// }
func (t compass) uploadSmartContract(
ctx context.Context,
msg *types.UploadSmartContract,
origMessage chain.MessageWithSignatures,
) error {
return whoops.Try(func() {
contractABI, err := abi.JSON(bytes.NewReader(msg.GetAbi()))
whoops.Assert(err)

// 0 means to get the latest valset
latestValset, err := t.paloma.QueryGetEVMValsetByID(ctx, 0, t.ChainID)
whoops.Assert(err)

consensusReached := isConsensusReached(latestValset, origMessage)
if !consensusReached {
whoops.Assert(ErrNoConsensus)
}

addr, tx, err := t.evm.DeployContract(ctx, contractABI, msg.GetBytecode(), msg.GetConstructorInput())
// TODO: do attestation
_ = addr
_ = tx
whoops.Assert(err)
return
})
}

func (t compass) findLastValsetMessageID(ctx context.Context) (uint64, error) {
filter := etherum.FilterQuery{
Expand Down Expand Up @@ -292,30 +291,48 @@ func buildConsensus(
func (t compass) processMessages(ctx context.Context, queueTypeName string, msgs []chain.MessageWithSignatures) error {
var gErr whoops.Group
for _, rawMsg := range msgs {
var processingErr error
logger := log.WithFields(log.Fields{
"processor-chain-id": t.ChainID,
"queue-name": queueTypeName,
"msg-id": rawMsg.ID,
})
logger.Info("processing")
msg := rawMsg.Msg.(*types.Message)

switch action := msg.GetAction().(type) {
case *types.Message_SubmitLogicCall:
err := t.submitLogicCall(
processingErr = t.submitLogicCall(
ctx,
rawMsg.ID,
action.SubmitLogicCall,
rawMsg,
)
gErr.Add(err)
case *types.Message_UpdateValset:
err := t.updateValset(
processingErr = t.updateValset(
ctx,
action.UpdateValset.Valset,
rawMsg,
)
gErr.Add(err)
case *types.Message_UploadSmartContract:
processingErr = t.uploadSmartContract(
ctx,
action.UploadSmartContract,
rawMsg,
)
default:
return ErrUnsupportedMessageType.Format(action)
}
// TODO: this is temporary
err := t.paloma.DeleteJob(ctx, queueTypeName, rawMsg.ID)
gErr.Add(err)

switch {
case processingErr == nil:
// TODO: this is temporary
err := t.paloma.DeleteJob(ctx, queueTypeName, rawMsg.ID)
gErr.Add(err)
case goerrors.Is(processingErr, ErrNoConsensus):
// does nothing
default:
gErr.Add(processingErr)
}
}

if gErr.Err() {
Expand Down
81 changes: 79 additions & 2 deletions chain/evm/compass_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ func TestMessageProcessing(t *testing.T) {
fn := args.Get(3).(func([]etherumtypes.Log) bool)
fn(isArbitraryCallExecutedLogs)
})

paloma.On("DeleteJob", mock.Anything, "queue-name", uint64(666)).Return(nil)

return evm, paloma
Expand Down Expand Up @@ -208,7 +207,6 @@ func TestMessageProcessing(t *testing.T) {
},
nil,
)
paloma.On("DeleteJob", mock.Anything, "queue-name", uint64(555)).Return(nil)

return evm, paloma
},
Expand Down Expand Up @@ -345,10 +343,89 @@ func TestMessageProcessing(t *testing.T) {
nil,
)

return evm, paloma
},
},
{
name: "upload_smart_contract/happy path",
msgs: []chain.MessageWithSignatures{
{
QueuedMessage: chain.QueuedMessage{
ID: 555,
BytesToSign: ethCompatibleBytesToSign,
Msg: &types.Message{
Action: &types.Message_UploadSmartContract{
UploadSmartContract: &types.UploadSmartContract{
Bytecode: []byte("bytecode"),
Abi: StoredContracts()["simple"].Source,
ConstructorInput: []byte("constructor input"),
},
},
},
},
Signatures: []chain.ValidatorSignature{
addValidSignature(bobPK),
},
},
},
setup: func(t *testing.T) (*mockEvmClienter, *mockPalomaClienter) {
evm, paloma := newMockEvmClienter(t), newMockPalomaClienter(t)

currentValsetID := int64(0)

paloma.On("QueryGetEVMValsetByID", mock.Anything, uint64(currentValsetID), "internal-chain-id").Return(
&types.Valset{
Validators: []string{crypto.PubkeyToAddress(bobPK.PublicKey).Hex()},
Powers: []uint64{powerThreshold + 1},
ValsetID: uint64(currentValsetID),
},
nil,
)

evm.On("DeployContract", mock.Anything, StoredContracts()["simple"].ABI, []byte("bytecode"), []byte("constructor input")).Return(nil, nil, nil)

paloma.On("DeleteJob", mock.Anything, "queue-name", uint64(555)).Return(nil)
return evm, paloma
},
},
{
name: "upload_smart_contract/without a consensus it returns an error",
msgs: []chain.MessageWithSignatures{
{
QueuedMessage: chain.QueuedMessage{
ID: 555,
BytesToSign: ethCompatibleBytesToSign,
Msg: &types.Message{
Action: &types.Message_UploadSmartContract{
UploadSmartContract: &types.UploadSmartContract{
Bytecode: []byte("bytecode"),
Abi: StoredContracts()["simple"].Source,
ConstructorInput: []byte("constructor input"),
},
},
},
},
Signatures: []chain.ValidatorSignature{
addValidSignature(bobPK),
},
},
},
setup: func(t *testing.T) (*mockEvmClienter, *mockPalomaClienter) {
evm, paloma := newMockEvmClienter(t), newMockPalomaClienter(t)

currentValsetID := int64(0)

paloma.On("QueryGetEVMValsetByID", mock.Anything, uint64(currentValsetID), "internal-chain-id").Return(
&types.Valset{
Validators: []string{crypto.PubkeyToAddress(bobPK.PublicKey).Hex()},
Powers: []uint64{5},
ValsetID: uint64(currentValsetID),
},
nil,
)
return evm, paloma
},
},
} {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
Expand Down
12 changes: 8 additions & 4 deletions chain/evm/deploy_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/vizualni/whoops"
)

func (c Client) DeployContract(ctx context.Context, contractAbi abi.ABI, bytecode []byte, constructorArgs []any) (contractAddr common.Address, tx *ethtypes.Transaction, err error) {
func (c Client) DeployContract(ctx context.Context, contractAbi abi.ABI, bytecode, constructorInput []byte) (contractAddr common.Address, tx *ethtypes.Transaction, err error) {
return deployContract(
ctx,
c.conn,
Expand All @@ -24,7 +24,7 @@ func (c Client) DeployContract(ctx context.Context, contractAbi abi.ABI, bytecod
c.config.GetChainID(),
contractAbi,
bytecode,
constructorArgs,
constructorInput,
)
}

Expand All @@ -36,11 +36,10 @@ func deployContract(
chainID *big.Int,
contractAbi abi.ABI,
bytecode []byte,
constructorArgs []any,
constructorInput []byte,
) (contractAddr common.Address, tx *ethtypes.Transaction, err error) {
logger := log.WithField("chainID", chainID)
err = whoops.Try(func() {

nonce, err := ethClient.PendingNonceAt(ctx, signingAddr)
whoops.Assert(err)

Expand All @@ -62,6 +61,11 @@ func deployContract(
"tx-opts": txOpts,
})

// hack begins here
constructorArgs, err := contractAbi.Constructor.Inputs.Unpack(constructorInput)
whoops.Assert(err)
// hack ends here

logger.Info("deploying contract")
contractAddr, tx, _, err = bind.DeployContract(
txOpts,
Expand Down
2 changes: 2 additions & 0 deletions chain/evm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ const (
ErrInvalidAddress = whoops.Errorf("provided address: '%s' is not valid")
ErrAddressNotFoundInKeyStore = whoops.Errorf("address: '%s' not found in keystore: %s")
ErrUnsupportedMessageType = whoops.Errorf("unsupported message type: %T")

ErrNoConsensus = whoops.String("no consensus reached")
)
32 changes: 32 additions & 0 deletions chain/evm/mock_evmClienter_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 46949b1

Please sign in to comment.