aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <tederminant@gmail.com>2020-08-19 23:34:12 -0400
committerDeterminant <tederminant@gmail.com>2020-08-19 23:34:12 -0400
commit40879ea67433b73b464bd8b012a9809fbb646cfd (patch)
treea11646c8f69a217c936322bd937915b2ecc7bdba
parent0b934ef60fd652de038a21e6f25691b11cea7121 (diff)
WIP: C-to-X transfer
-rw-r--r--plugin/evm/import_tx.go16
-rw-r--r--plugin/evm/service.go3
-rw-r--r--plugin/evm/tx.go37
-rw-r--r--plugin/evm/user.go4
-rw-r--r--plugin/evm/vm.go61
5 files changed, 68 insertions, 53 deletions
diff --git a/plugin/evm/import_tx.go b/plugin/evm/import_tx.go
index 2e49493..68f34b0 100644
--- a/plugin/evm/import_tx.go
+++ b/plugin/evm/import_tx.go
@@ -27,6 +27,7 @@ var (
errUnknownAsset = errors.New("unknown asset ID")
errNoFunds = errors.New("no spendable funds were found")
errWrongChainID = errors.New("tx has wrong chain ID")
+ errInsufficientFunds = errors.New("insufficient funds")
)
// UnsignedImportTx is an unsigned ImportTx
@@ -36,19 +37,16 @@ type UnsignedImportTx struct {
syntacticallyVerified bool
// ID of the network on which this tx was issued
NetworkID uint32 `serialize:"true" json:"networkID"`
- // ID of this blockchain. In practice is always the empty ID.
- // This is only here to match avm.BaseTx's format
+ // ID of this blockchain.
BlockchainID ids.ID `serialize:"true" json:"blockchainID"`
- // Outputs
- Outs []EVMOutput `serialize:"true" json:"outputs"`
- // Memo field contains arbitrary bytes, up to maxMemoSize
- Memo []byte `serialize:"true" json:"memo"`
-
// Which chain to consume the funds from
SourceChain ids.ID `serialize:"true" json:"sourceChain"`
-
// Inputs that consume UTXOs produced on the chain
ImportedInputs []*avax.TransferableInput `serialize:"true" json:"importedInputs"`
+ // Outputs
+ Outs []EVMOutput `serialize:"true" json:"outputs"`
+ // Memo field contains arbitrary bytes, up to maxMemoSize
+ Memo []byte `serialize:"true" json:"memo"`
}
// InputUTXOs returns the UTXOIDs of the imported funds
@@ -147,7 +145,7 @@ func (vm *VM) newImportTx(
to common.Address, // Address of recipient
keys []*crypto.PrivateKeySECP256K1R, // Keys to import the funds
) (*Tx, error) {
- if !vm.avm.Equals(chainID) {
+ if !vm.ctx.XChainID.Equals(chainID) {
return nil, errWrongChainID
}
diff --git a/plugin/evm/service.go b/plugin/evm/service.go
index 86bc706..3165ca7 100644
--- a/plugin/evm/service.go
+++ b/plugin/evm/service.go
@@ -201,8 +201,7 @@ func (service *AvaAPI) ImportKey(r *http.Request, args *ImportKeyArgs, reply *ap
}
// TODO: return eth address here
- reply.Address, err = service.vm.FormatAddress(
- ethcrypto.PubkeyToAddress(*(sk.PublicKey().(*crypto.PublicKeySECP256K1R).ToECDSA())))
+ reply.Address, err = service.vm.FormatAddress(GetEthAddress(sk))
if err != nil {
return fmt.Errorf("problem formatting address: %w", err)
}
diff --git a/plugin/evm/tx.go b/plugin/evm/tx.go
index 6196eb8..90cd232 100644
--- a/plugin/evm/tx.go
+++ b/plugin/evm/tx.go
@@ -8,7 +8,6 @@ import (
"fmt"
"github.com/ava-labs/gecko/database"
- "github.com/ava-labs/gecko/database/versiondb"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow"
"github.com/ava-labs/gecko/utils/codec"
@@ -38,6 +37,12 @@ func (out *EVMOutput) Verify() error {
return nil
}
+type EVMInput EVMOutput
+
+func (in *EVMInput) Verify() error {
+ return nil
+}
+
// UnsignedTx is an unsigned transaction
type UnsignedTx interface {
Initialize(unsignedBytes, signedBytes []byte)
@@ -46,32 +51,6 @@ type UnsignedTx interface {
Bytes() []byte
}
-// UnsignedDecisionTx is an unsigned operation that can be immediately decided
-type UnsignedDecisionTx interface {
- UnsignedTx
-
- // Attempts to verify this transaction with the provided state.
- SemanticVerify(vm *VM, db database.Database, stx *Tx) (
- onAcceptFunc func() error,
- err TxError,
- )
-}
-
-// UnsignedProposalTx is an unsigned operation that can be proposed
-type UnsignedProposalTx interface {
- UnsignedTx
-
- // Attempts to verify this transaction with the provided state.
- SemanticVerify(vm *VM, db database.Database, stx *Tx) (
- onCommitDB *versiondb.Database,
- onAbortDB *versiondb.Database,
- onCommitFunc func() error,
- onAbortFunc func() error,
- err TxError,
- )
- InitiallyPrefersCommit(vm *VM) bool
-}
-
// UnsignedAtomicTx is an unsigned operation that can be atomically accepted
type UnsignedAtomicTx interface {
UnsignedTx
@@ -79,7 +58,7 @@ type UnsignedAtomicTx interface {
// UTXOs this tx consumes
InputUTXOs() ids.Set
// Attempts to verify this transaction with the provided state.
- SemanticVerify(vm *VM, db database.Database, stx *Tx) TxError
+ SemanticVerify(vm *VM, stx *Tx) TxError
// Accept this transaction with the additionally provided state transitions.
Accept(ctx *snow.Context, batch database.Batch) error
@@ -94,6 +73,8 @@ type Tx struct {
Creds []verify.Verifiable `serialize:"true" json:"credentials"`
}
+// (*secp256k1fx.Credential)
+
// Sign this transaction with the provided signers
func (tx *Tx) Sign(c codec.Codec, signers [][]*crypto.PrivateKeySECP256K1R) error {
unsignedBytes, err := c.Marshal(&tx.UnsignedTx)
diff --git a/plugin/evm/user.go b/plugin/evm/user.go
index 5d7a037..fbf2981 100644
--- a/plugin/evm/user.go
+++ b/plugin/evm/user.go
@@ -11,7 +11,6 @@ import (
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/go-ethereum/common"
- ethcrypto "github.com/ava-labs/go-ethereum/crypto"
)
// Key in the database whose corresponding value is the list of
@@ -72,8 +71,7 @@ func (u *user) putAddress(privKey *crypto.PrivateKeySECP256K1R) error {
return errKeyNil
}
- address := ethcrypto.PubkeyToAddress(
- (*privKey.PublicKey().(*crypto.PublicKeySECP256K1R).ToECDSA())) // address the privKey controls
+ address := GetEthAddress(privKey) // address the privKey controls
controlsAddress, err := u.controlsAddress(address)
if err != nil {
return err
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index baf6106..5bb8ac0 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -22,6 +22,7 @@ import (
"github.com/ava-labs/coreth/node"
"github.com/ava-labs/go-ethereum/common"
+ ethcrypto "github.com/ava-labs/go-ethereum/crypto"
"github.com/ava-labs/go-ethereum/rlp"
"github.com/ava-labs/go-ethereum/rpc"
avarpc "github.com/gorilla/rpc/v2"
@@ -34,6 +35,7 @@ import (
"github.com/ava-labs/gecko/snow/choices"
"github.com/ava-labs/gecko/snow/consensus/snowman"
"github.com/ava-labs/gecko/utils/codec"
+ "github.com/ava-labs/gecko/utils/crypto"
avajson "github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/timer"
"github.com/ava-labs/gecko/utils/wrappers"
@@ -48,6 +50,7 @@ var (
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
+ x2cRate = big.NewInt(1000000000)
)
const (
@@ -160,6 +163,15 @@ func (vm *VM) getAtomicTx(block *types.Block) *Tx {
return atx
}
+func importTxStateTransfer(tx *UnsignedImportTx, state *state.StateDB) {
+ for _, to := range tx.Outs {
+ amount := new(big.Int).SetUint64(to.Amount)
+ state.AddBalance(to.Address, new(big.Int).Mul(amount, x2cRate))
+ nonce := state.GetNonce(to.Address)
+ state.SetNonce(to.Address, nonce+1)
+ }
+}
+
/*
******************************************************************************
********************************* Snowman API ********************************
@@ -213,11 +225,7 @@ func (vm *VM) Initialize(
chain.SetOnFinalizeAndAssemble(func(state *state.StateDB, txs []*types.Transaction) ([]byte, error) {
select {
case atx := <-vm.pendingAtomicTxs:
- for _, to := range atx.UnsignedTx.(*UnsignedImportTx).Outs {
- amount := new(big.Int)
- amount.SetUint64(to.Amount)
- state.AddBalance(to.Address, amount)
- }
+ importTxStateTransfer(atx.UnsignedTx.(*UnsignedImportTx), state)
raw, _ := vm.codec.Marshal(atx)
return raw, nil
default:
@@ -250,13 +258,9 @@ func (vm *VM) Initialize(
chain.SetOnQueryAcceptedBlock(func() *types.Block {
return vm.getLastAccepted().ethBlock
})
- chain.SetOnExtraStateChange(func(block *types.Block, statedb *state.StateDB) error {
+ chain.SetOnExtraStateChange(func(block *types.Block, state *state.StateDB) error {
atx := vm.getAtomicTx(block).UnsignedTx.(*UnsignedImportTx)
- for _, to := range atx.Outs {
- amount := new(big.Int)
- amount.SetUint64(to.Amount)
- statedb.AddBalance(to.Address, amount)
- }
+ importTxStateTransfer(atx, state)
return nil
})
vm.blockCache = cache.LRU{Size: 2048}
@@ -683,3 +687,38 @@ func (vm *VM) GetAtomicUTXOs(
}}
return utxos, ids.ShortEmpty, ids.Empty, nil
}
+
+func GetEthAddress(privKey *crypto.PrivateKeySECP256K1R) common.Address {
+ return ethcrypto.PubkeyToAddress(
+ (*privKey.PublicKey().(*crypto.PublicKeySECP256K1R).ToECDSA()))
+}
+
+func (vm *VM) GetSpendableCanonical(keys []*crypto.PrivateKeySECP256K1R, amount uint64) ([]EVMInput, [][]*crypto.PrivateKeySECP256K1R, error) {
+ // NOTE: should we use HEAD block or lastAccepted?
+ state, err := vm.chain.BlockState(vm.lastAccepted.ethBlock)
+ if err != nil {
+ return nil, nil, err
+ }
+ inputs := []EVMInput{}
+ signers := [][]*crypto.PrivateKeySECP256K1R{}
+ for _, key := range keys {
+ if amount == 0 {
+ break
+ }
+ addr := GetEthAddress(key)
+ balance := new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64()
+ if amount < balance {
+ balance = amount
+ }
+ inputs = append(inputs, EVMInput{
+ Address: addr,
+ Amount: balance,
+ })
+ signers = append(signers, []*crypto.PrivateKeySECP256K1R{key})
+ amount -= balance
+ }
+ if amount > 0 {
+ return nil, nil, errInsufficientFunds
+ }
+ return inputs, signers, nil
+}