Skip to content
Draft
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ BINANCE_TGORACLE_DIR=$(GOPATH)/src/$(PKG_BINANCE_TGORACLE)
# NOTE: To build on Jenkins using a custom go-loom branch update the `deps` target below to checkout
# that branch, you only need to update GO_LOOM_GIT_REV if you wish to lock the build to a
# specific commit.
GO_LOOM_GIT_REV = HEAD
GO_LOOM_GIT_REV = tx-router-cfg
# Specifies the loomnetwork/transfer-gateway branch/revision to use.
TG_GIT_REV = HEAD
# loomnetwork/go-ethereum loomchain branch
Expand Down
52 changes: 52 additions & 0 deletions cmd/loom/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
proto "github.com/gogo/protobuf/proto"
loom "github.com/loomnetwork/go-loom"
lauth "github.com/loomnetwork/go-loom/auth"
cctypes "github.com/loomnetwork/go-loom/builtin/types/chainconfig"
"github.com/loomnetwork/go-loom/types"
"github.com/loomnetwork/loomchain"
"github.com/loomnetwork/loomchain/auth"
Expand Down Expand Up @@ -65,6 +66,57 @@ func TestTxHandlerWithInvalidCaller(t *testing.T) {
require.True(t, strings.HasPrefix(err.Error(), "Origin doesn't match caller"))
}

func TestSingleRouteTxHandlerWithInvalidCaller(t *testing.T) {
_, alicePrivKey, err := ed25519.GenerateKey(nil)
require.NoError(t, err)

bobPubKey, _, err := ed25519.GenerateKey(nil)
require.NoError(t, err)

createRegistry, err := registry.NewRegistryFactory(registry.LatestRegistryVersion)
require.NoError(t, err)

vmManager := vm.NewManager()
router := loomchain.NewTxRouter()
router.Handle(1, &vm.DeployTxHandler{Manager: vmManager, CreateRegistry: createRegistry})
router.Handle(2, &vm.CallTxHandler{Manager: vmManager})

kvStore := store.NewMemStore()
state := loomchain.NewStoreState(nil, kvStore, abci.Header{ChainID: "default"}, nil, nil)
require.NoError(t, store.SaveOnChainConfig(kvStore, &cctypes.Config{
TxRouter: &cctypes.TxRouterConfig{
UseSingleRoute: true,
},
}))

txMiddleWare := []loomchain.TxMiddleware{
auth.SignatureTxMiddleware,
auth.NewNonceHandler().TxMiddleware(kvStore),
}

rootHandler := loomchain.MiddlewareTxHandler(txMiddleWare, router, nil)
signer := lauth.NewEd25519Signer(alicePrivKey)
caller := loom.Address{
ChainID: "default",
Local: loom.LocalAddressFromPublicKey(bobPubKey),
}

// Try to process txs in which Alice attempts to impersonate Bob
_, err = rootHandler.ProcessTx(state, createTxWithInvalidCaller(t, signer, caller, &vm.DeployTx{
VmType: vm.VMType_PLUGIN,
Code: nil,
Name: "hello",
}, 1, 1), false)
require.Error(t, err)
require.True(t, strings.HasPrefix(err.Error(), "Origin doesn't match caller"))

_, err = rootHandler.ProcessTx(state, createTxWithInvalidCaller(t, signer, caller, &vm.CallTx{
VmType: vm.VMType_PLUGIN,
}, 2, 2), false)
require.Error(t, err)
require.True(t, strings.HasPrefix(err.Error(), "Origin doesn't match caller"))
}

func createTxWithInvalidCaller(t *testing.T, signer lauth.Signer, caller loom.Address,
tx proto.Message, txType uint32, nonce uint64) []byte {
payload, err := proto.Marshal(tx)
Expand Down
14 changes: 14 additions & 0 deletions cmd/loom/loom.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,8 @@ func loadApp(

router := loomchain.NewTxRouter()

// legacy router setup

isEvmTx := func(txID uint32, state loomchain.State, txBytes []byte, isCheckTx bool) bool {
var msg vm.MessageTx
err := proto.Unmarshal(txBytes, &msg)
Expand Down Expand Up @@ -921,6 +923,18 @@ func loadApp(
router.HandleCheckTx(2, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, callTxHandler))
router.HandleCheckTx(3, loomchain.GenerateConditionalRouteHandler(isEvmTx, loomchain.NoopTxHandler, migrationTxHandler))

// non-legacy router setup

router.Handle(1, &tx_handler.DeployTxHandler{
Manager: vmManager,
CreateRegistry: createRegistry,
AllowNamedEVMContracts: cfg.AllowNamedEvmContracts,
})
router.Handle(2, &tx_handler.CallTxHandler{
Manager: vmManager,
})
router.Handle(3, migrationTxHandler)

txMiddleWare := []loomchain.TxMiddleware{
loomchain.LogTxMiddleware,
loomchain.RecoveryTxMiddleware,
Expand Down
16 changes: 15 additions & 1 deletion router.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
type Transaction = types.Transaction

type TxRouter struct {
routes map[uint32]RouteHandler
// legacy, will be removed in a future release
deliverTxRoutes map[uint32]RouteHandler
checkTxRoutes map[uint32]RouteHandler
}
Expand All @@ -34,11 +36,20 @@ func GenerateConditionalRouteHandler(conditionFn RouteConditionFunc, onTrue TxHa

func NewTxRouter() *TxRouter {
return &TxRouter{
routes: make(map[uint32]RouteHandler),
deliverTxRoutes: make(map[uint32]RouteHandler),
checkTxRoutes: make(map[uint32]RouteHandler),
}
}

func (r *TxRouter) Handle(txID uint32, handler TxHandler) {
if _, ok := r.routes[txID]; ok {
panic("handler for transaction already registered")
}
// TODO: remove the GeneratePassthroughRouteHandler once the deliver/checkTxRoutes are gone
r.routes[txID] = GeneratePassthroughRouteHandler(handler)
Comment thread
pathornteng marked this conversation as resolved.
}

func (r *TxRouter) HandleDeliverTx(txID uint32, handler RouteHandler) {
if _, ok := r.deliverTxRoutes[txID]; ok {
panic("handler for transaction already registered")
Expand All @@ -65,7 +76,10 @@ func (r *TxRouter) ProcessTx(state State, txBytes []byte, isCheckTx bool) (TxHan
}

var routeHandler RouteHandler
if isCheckTx {

if state.Config().GetTxRouter().UseSingleRoute {
routeHandler = r.routes[tx.Id]
} else if isCheckTx {
routeHandler = r.checkTxRoutes[tx.Id]
} else {
routeHandler = r.deliverTxRoutes[tx.Id]
Expand Down
75 changes: 75 additions & 0 deletions tx_handler/call_tx_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package tx_handler

import (
"fmt"

"github.com/gogo/protobuf/proto"
"github.com/loomnetwork/go-loom"
"github.com/loomnetwork/loomchain"
"github.com/loomnetwork/loomchain/auth"
"github.com/loomnetwork/loomchain/eth/utils"
"github.com/loomnetwork/loomchain/vm"
"github.com/pkg/errors"
)

// CallTxHandler handles txs that call Go & EVM contracts
type CallTxHandler struct {
*vm.Manager
}

func (h *CallTxHandler) ProcessTx(
state loomchain.State,
txBytes []byte,
isCheckTx bool,
) (loomchain.TxHandlerResult, error) {
var r loomchain.TxHandlerResult

var msg vm.MessageTx
if err := proto.Unmarshal(txBytes, &msg); err != nil {
return r, err
}

origin := auth.Origin(state.Context())
caller := loom.UnmarshalAddressPB(msg.From)
addr := loom.UnmarshalAddressPB(msg.To)

if caller.Compare(origin) != 0 {
return r, fmt.Errorf("Origin doesn't match caller: %v != %v", origin, caller)
}

// TODO: move the marshalling & validation above this line into middleware
var tx vm.CallTx
if err := proto.Unmarshal(msg.Data, &tx); err != nil {
return r, err
}

switch tx.VmType {
case vm.VMType_EVM:
r.Info = utils.CallEVM
// Only do basic validation of EVM calls in CheckTx, don't execute the actual call
if isCheckTx {
return r, nil
}

case vm.VMType_PLUGIN:
r.Info = utils.CallPlugin

default:
return r, errors.New("invalid vm type")
}

vmInstance, err := h.Manager.InitVM(tx.VmType, state)
if err != nil {
return r, err
}

var value *loom.BigUInt
if tx.Value == nil {
value = loom.NewBigUIntFromInt(0)
} else {
value = &tx.Value.Value
}

r.Data, err = vmInstance.Call(origin, addr, tx.Input, value)
return r, err
}
103 changes: 103 additions & 0 deletions tx_handler/deploy_tx_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package tx_handler

import (
"fmt"

"github.com/gogo/protobuf/proto"
"github.com/loomnetwork/go-loom"
"github.com/loomnetwork/go-loom/types"
"github.com/loomnetwork/loomchain"
"github.com/loomnetwork/loomchain/auth"
"github.com/loomnetwork/loomchain/eth/utils"
registry "github.com/loomnetwork/loomchain/registry/factory"
"github.com/loomnetwork/loomchain/vm"
"github.com/pkg/errors"
)

// DeployTxHandler handles txs that deploy Go & EVM contracts
type DeployTxHandler struct {
*vm.Manager
CreateRegistry registry.RegistryFactoryFunc
AllowNamedEVMContracts bool
}

func (h *DeployTxHandler) ProcessTx(
state loomchain.State,
txBytes []byte,
isCheckTx bool,
) (loomchain.TxHandlerResult, error) {
var r loomchain.TxHandlerResult

var msg vm.MessageTx
if err := proto.Unmarshal(txBytes, &msg); err != nil {
return r, err
}

origin := auth.Origin(state.Context())
caller := loom.UnmarshalAddressPB(msg.From)

if caller.Compare(origin) != 0 {
return r, fmt.Errorf("Origin doesn't match caller: - %v != %v", origin, caller)
}

// TODO: move the marshalling & validation above this line into middleware
var tx vm.DeployTx
if err := proto.Unmarshal(msg.Data, &tx); err != nil {
return r, err
}

switch tx.VmType {
case vm.VMType_EVM:
r.Info = utils.DeployEvm

if (len(tx.Name) > 0) && !h.AllowNamedEVMContracts {
return r, errors.New("named evm contracts are not allowed")
}

// Only do basic validation of EVM deploys in CheckTx, don't execute the actual deploy
if isCheckTx {
return r, nil
}

case vm.VMType_PLUGIN:
r.Info = utils.DeployPlugin

default:
return r, errors.New("invalid vm type")
}

vmInstance, err := h.Manager.InitVM(tx.VmType, state)
if err != nil {
return r, err
}

var value *loom.BigUInt
if tx.Value == nil {
value = loom.NewBigUIntFromInt(0)
} else {
value = &tx.Value.Value
}

retCreate, addr, err := vmInstance.Create(origin, tx.Code, value)
if err != nil {
return r, errors.Wrapf(err, "failed to create contract")
}

response, err := proto.Marshal(&vm.DeployResponse{
Contract: &types.Address{
ChainId: addr.ChainID,
Local: addr.Local,
},
Output: retCreate,
})
if err != nil {
return r, errors.Wrapf(err, "failed to marshal deploy response")
}
r.Data = response

reg := h.CreateRegistry(state)
if err := reg.Register(tx.Name, addr, caller); err != nil {
return r, err
}
return r, nil
}