From 2114e646f4e5736edd0e1cca55d7b16b20a67fdb Mon Sep 17 00:00:00 2001 From: Juan Perez Date: Thu, 14 Nov 2019 18:15:50 +0100 Subject: [PATCH] Electrum fork --- consensus/ethash/consensus.go | 4 +- core/evm.go | 63 +++++++++- core/state/statedb.go | 213 +++++++++++++++++++++++----------- core/state_transition.go | 161 +++++++++++++++++++++++-- core/vm/evm.go | 30 +++-- eth/backend.go | 8 +- 6 files changed, 382 insertions(+), 97 deletions(-) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index d271518f4f..a1f5e8bb05 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -628,10 +628,10 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, big8) - state.AddBalance(uncle.Coinbase, r) + // state.AddBalance(uncle.Coinbase, r) // inflationary rewards removed by LydianElectrum r.Div(blockReward, big32) reward.Add(reward, r) } - state.AddBalance(header.Coinbase, reward) + // state.AddBalance(header.Coinbase, reward) // inflationary rewards removed by LydianElectrum } diff --git a/core/evm.go b/core/evm.go index b654bbd479..f160cb6b10 100644 --- a/core/evm.go +++ b/core/evm.go @@ -18,6 +18,8 @@ package core import ( "math/big" + "fmt" + "encoding/hex" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" @@ -91,7 +93,62 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { } // Transfer subtracts amount from sender and adds amount to recipient using the given Db -func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { - db.SubBalance(sender, amount) - db.AddBalance(recipient, amount) +// func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { // LydianElectrum requires knowledge of evm, not just evm.StateDB +func Transfer(evm *vm.EVM, sender, recipient common.Address, amount *big.Int) { + // db.SubBalance(sender, amount) // LydianElectrum + // evm.StateDB.SubBalance(sender, amount) // LydianElectrum: an ERC-20 transfer overrides this operation + // db.AddBalance(recipient, amount) // LydianElectrum + // evm.StateDB.AddBalance(recipient, amount) // LydianElectrum: an ERC-20 transfer overrides this operation + + // LydianElectrum: CryptoEuro is an ERC-20 SC deployed on bootstrapping. Being the first one deployed, its address becomes predictable + address := common.HexToAddress("0x88e726de6cbadc47159c6ccd4f7868ae7a037730") // LydianElectrum: CriptoEuro contract hardcoded address + contract := vm.AccountRef(address) + // caller := vm.AccountRef(sender) + + // methodHash := "a9059cbb" // transfer method + // methodHash := "9063e860" // transferOrigin method + methodHash := "222f5be0" // transferInternal method + // addressTo := "ca35b7d915458ef540ade6068dfe2f44e8fa733c" // con o sin left padding de leading zeros + addressTo := recipient.String()[2:] // removing leading 0x + addressSender := sender.String()[2:] // removing leading 0x + + // padding del addressTo + for len(addressTo) < 64 { addressTo = "0" + addressTo } + + // padding del addressSender + for len(addressSender) < 64 { addressSender = "0" + addressSender } + + // convertimos la cantidad a hexadecimal + amountStr := fmt.Sprintf("%x", amount) + + // padding + for len(amountStr) < 64 { + amountStr = "0" + amountStr + } + + // juntamos todo en una cadena hexadecimal (SIN el 0x delante) + // inputDataHex := methodHash + addressTo + amountStr + inputDataHex := methodHash + addressSender + addressTo + amountStr + + fmt.Println("inputDataHex: ", inputDataHex) + + // lo convertimos de hexadecimal a []byte que es lo que necesitamos + inputData, err := hex.DecodeString(inputDataHex) + + gas := uint64(3000000) + value := new(big.Int) + + fmt.Println("sender: ", sender) + fmt.Println("senderString: ", sender.String()) + fmt.Println("addressSender: ", addressSender) + fmt.Println("addressTo: ", addressTo) + fmt.Println("amountStr: ", amountStr) + fmt.Println("address: ", address) + + // LydianElectrum: this is the ERC-20 transfer that overrides native coin operations + ret, returnGas, err := evm.CallCode(contract, address, inputData, gas, value) + + fmt.Println("ret: ", ret) + fmt.Println("returnGas: ", returnGas) + fmt.Println("err: ", err) } diff --git a/core/state/statedb.go b/core/state/statedb.go index 3bb9862edd..a394856ade 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -20,6 +20,7 @@ package state import ( "errors" "fmt" + "encoding/hex" "math/big" "sort" "time" @@ -67,8 +68,9 @@ type StateDB struct { trie Trie // This map holds 'live' objects, which will get modified while processing a state transition. - stateObjects map[common.Address]*stateObject - stateObjectsDirty map[common.Address]struct{} + stateObjects map[common.Address]*stateObject + stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie + stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution // DB error. // State objects are used by the consensus core and VM which are @@ -111,13 +113,14 @@ func New(root common.Hash, db Database) (*StateDB, error) { return nil, err } return &StateDB{ - db: db, - trie: tr, - stateObjects: make(map[common.Address]*stateObject), - stateObjectsDirty: make(map[common.Address]struct{}), - logs: make(map[common.Hash][]*types.Log), - preimages: make(map[common.Hash][]byte), - journal: newJournal(), + db: db, + trie: tr, + stateObjects: make(map[common.Address]*stateObject), + stateObjectsPending: make(map[common.Address]struct{}), + stateObjectsDirty: make(map[common.Address]struct{}), + logs: make(map[common.Hash][]*types.Log), + preimages: make(map[common.Hash][]byte), + journal: newJournal(), }, nil } @@ -141,6 +144,7 @@ func (self *StateDB) Reset(root common.Hash) error { } self.trie = tr self.stateObjects = make(map[common.Address]*stateObject) + self.stateObjectsPending = make(map[common.Address]struct{}) self.stateObjectsDirty = make(map[common.Address]struct{}) self.thash = common.Hash{} self.bhash = common.Hash{} @@ -221,11 +225,44 @@ func (self *StateDB) Empty(addr common.Address) bool { // Retrieve the balance from the given address or 0 if object not found func (self *StateDB) GetBalance(addr common.Address) *big.Int { - stateObject := self.getStateObject(addr) +// LydianElectrum: Instead of getting the account balance from the ledger, retrieve it from the CryptoEuro ERC-20 Smart Contract +// To query the contract while lacking an EVM reference, we go directly through contract storage +/* stateObject := self.getStateObject(addr) if stateObject != nil { +fmt.Println("stateObject.Balance(): ", stateObject.Balance()) return stateObject.Balance() } return common.Big0 +*/ + + // Debug: stateObject + contractAddr := common.HexToAddress("0x88e726de6cbadc47159c6ccd4f7868ae7a037730") // LydianElectrum: CryptoEuro contract hardcoded + // contractStateObject := self.getStateObject(contractAddr) + // fmt.Println("contractStateObject: ", contractStateObject) + + // Debug: contract storage + // Crunching hash index for contract token balance at address 0x8703f7b22fc5613497aee971e80480a46b226d3c // x must be replaced by 0 + // key := "0000000000000000000000008703f7b22fc5613497aee971e80480a46b226d3c" // x must be replaced by 0 + key := "000000000000000000000000" + hex.EncodeToString(addr.Bytes()) + index := "0000000000000000000000000000000000000000000000000000000000000001" + decoded, err := hex.DecodeString(key + index) // No 0x padding for GoLang keccak256 implementation + if err != nil { + fmt.Println("err: ", err) + } + fmt.Println("decoded: ", hex.EncodeToString(decoded)) + var newKey = crypto.Keccak256(decoded) + fmt.Println("newKey: ", hex.EncodeToString(newKey)) + + var h common.Hash + h.SetBytes(newKey) + getStateNewKey := self.GetState(contractAddr, h) + fmt.Println("getStateNewKey: ", getStateNewKey) + + // Convert []byte into big.Int + z := getStateNewKey.Big() + fmt.Println("z: ", z) + + return z } func (self *StateDB) GetNonce(addr common.Address) uint64 { @@ -386,6 +423,15 @@ func (self *StateDB) SetState(addr common.Address, key, value common.Hash) { } } +// SetStorage replaces the entire storage for the specified account with given +// storage. This function should only be used for debugging. +func (self *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) { + stateObject := self.GetOrNewStateObject(addr) + if stateObject != nil { + stateObject.SetStorage(storage) + } +} + // Suicide marks the given account as suicided. // This clears the account balance. // @@ -412,15 +458,15 @@ func (self *StateDB) Suicide(addr common.Address) bool { // // updateStateObject writes the given object to the trie. -func (s *StateDB) updateStateObject(stateObject *stateObject) { +func (s *StateDB) updateStateObject(obj *stateObject) { // Track the amount of time wasted on updating the account from the trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) } // Encode the account and update the account trie - addr := stateObject.Address() + addr := obj.Address() - data, err := rlp.EncodeToBytes(stateObject) + data, err := rlp.EncodeToBytes(obj) if err != nil { panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) } @@ -428,25 +474,33 @@ func (s *StateDB) updateStateObject(stateObject *stateObject) { } // deleteStateObject removes the given object from the state trie. -func (s *StateDB) deleteStateObject(stateObject *stateObject) { +func (s *StateDB) deleteStateObject(obj *stateObject) { // Track the amount of time wasted on deleting the account from the trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) } // Delete the account from the trie - stateObject.deleted = true - - addr := stateObject.Address() + addr := obj.Address() s.setError(s.trie.TryDelete(addr[:])) } -// Retrieve a state object given by the address. Returns nil if not found. -func (s *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) { - // Prefer live objects +// getStateObject retrieves a state object given by the address, returning nil if +// the object is not found or was deleted in this execution context. If you need +// to differentiate between non-existent/just-deleted, use getDeletedStateObject. +func (s *StateDB) getStateObject(addr common.Address) *stateObject { + if obj := s.getDeletedStateObject(addr); obj != nil && !obj.deleted { + return obj + } + return nil +} + +// getDeletedStateObject is similar to getStateObject, but instead of returning +// nil for a deleted state object, it returns the actual object with the deleted +// flag set. This is needed by the state journal to revert to the correct self- +// destructed object instead of wiping all knowledge about the state object. +func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { + // Prefer live objects if any is available if obj := s.stateObjects[addr]; obj != nil { - if obj.deleted { - return nil - } return obj } // Track the amount of time wasted on loading the object from the database @@ -477,7 +531,7 @@ func (self *StateDB) setStateObject(object *stateObject) { // Retrieve a state object or create a new state object if nil. func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { stateObject := self.getStateObject(addr) - if stateObject == nil || stateObject.deleted { + if stateObject == nil { stateObject, _ = self.createObject(addr) } return stateObject @@ -486,7 +540,8 @@ func (self *StateDB) GetOrNewStateObject(addr common.Address) *stateObject { // createObject creates a new state object. If there is an existing account with // the given address, it is overwritten and returned as the second return value. func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) { - prev = self.getStateObject(addr) + prev = self.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that! + newobj = newObject(self, addr, Account{}) newobj.setNonce(0) // sets the object to dirty if prev == nil { @@ -549,15 +604,16 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common func (self *StateDB) Copy() *StateDB { // Copy all the basic fields, initialize the memory ones state := &StateDB{ - db: self.db, - trie: self.db.CopyTrie(self.trie), - stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)), - stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)), - refund: self.refund, - logs: make(map[common.Hash][]*types.Log, len(self.logs)), - logSize: self.logSize, - preimages: make(map[common.Hash][]byte, len(self.preimages)), - journal: newJournal(), + db: self.db, + trie: self.db.CopyTrie(self.trie), + stateObjects: make(map[common.Address]*stateObject, len(self.journal.dirties)), + stateObjectsPending: make(map[common.Address]struct{}, len(self.stateObjectsPending)), + stateObjectsDirty: make(map[common.Address]struct{}, len(self.journal.dirties)), + refund: self.refund, + logs: make(map[common.Hash][]*types.Log, len(self.logs)), + logSize: self.logSize, + preimages: make(map[common.Hash][]byte, len(self.preimages)), + journal: newJournal(), } // Copy the dirty states, logs, and preimages for addr := range self.journal.dirties { @@ -566,18 +622,29 @@ func (self *StateDB) Copy() *StateDB { // in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for // nil if object, exist := self.stateObjects[addr]; exist { + // Even though the original object is dirty, we are not copying the journal, + // so we need to make sure that anyside effect the journal would have caused + // during a commit (or similar op) is already applied to the copy. state.stateObjects[addr] = object.deepCopy(state) - state.stateObjectsDirty[addr] = struct{}{} + + state.stateObjectsDirty[addr] = struct{}{} // Mark the copy dirty to force internal (code/state) commits + state.stateObjectsPending[addr] = struct{}{} // Mark the copy pending to force external (account) commits } } // Above, we don't copy the actual journal. This means that if the copy is copied, the // loop above will be a no-op, since the copy's journal is empty. // Thus, here we iterate over stateObjects, to enable copies of copies + for addr := range self.stateObjectsPending { + if _, exist := state.stateObjects[addr]; !exist { + state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) + } + state.stateObjectsPending[addr] = struct{}{} + } for addr := range self.stateObjectsDirty { if _, exist := state.stateObjects[addr]; !exist { state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state) - state.stateObjectsDirty[addr] = struct{}{} } + state.stateObjectsDirty[addr] = struct{}{} } for hash, logs := range self.logs { cpy := make([]*types.Log, len(logs)) @@ -622,11 +689,12 @@ func (self *StateDB) GetRefund() uint64 { return self.refund } -// Finalise finalises the state by removing the self destructed objects -// and clears the journal as well as the refunds. +// Finalise finalises the state by removing the self destructed objects and clears +// the journal as well as the refunds. Finalise, however, will not push any updates +// into the tries just yet. Only IntermediateRoot or Commit will do that. func (s *StateDB) Finalise(deleteEmptyObjects bool) { for addr := range s.journal.dirties { - stateObject, exist := s.stateObjects[addr] + obj, exist := s.stateObjects[addr] if !exist { // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 // That tx goes out of gas, and although the notion of 'touched' does not exist there, the @@ -636,13 +704,12 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // Thus, we can safely ignore it here continue } - - if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { - s.deleteStateObject(stateObject) + if obj.suicided || (deleteEmptyObjects && obj.empty()) { + obj.deleted = true } else { - stateObject.updateRoot(s.db) - s.updateStateObject(stateObject) + obj.finalise() } + s.stateObjectsPending[addr] = struct{}{} s.stateObjectsDirty[addr] = struct{}{} } // Invalidate journal because reverting across transactions is not allowed. @@ -653,8 +720,21 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // It is called in between transactions to get the root hash that // goes into transaction receipts. func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { + // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) + for addr := range s.stateObjectsPending { + obj := s.stateObjects[addr] + if obj.deleted { + s.deleteStateObject(obj) + } else { + obj.updateRoot(s.db) + s.updateStateObject(obj) + } + } + if len(s.stateObjectsPending) > 0 { + s.stateObjectsPending = make(map[common.Address]struct{}) + } // Track the amount of time wasted on hashing the account trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) @@ -671,46 +751,40 @@ func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) { } func (s *StateDB) clearJournalAndRefund() { - s.journal = newJournal() - s.validRevisions = s.validRevisions[:0] - s.refund = 0 + if len(s.journal.entries) > 0 { + s.journal = newJournal() + s.refund = 0 + } + s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entires } // Commit writes the state to the underlying in-memory trie database. -func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { - defer s.clearJournalAndRefund() +func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { + // Finalize any pending changes and merge everything into the tries + s.IntermediateRoot(deleteEmptyObjects) - for addr := range s.journal.dirties { - s.stateObjectsDirty[addr] = struct{}{} - } // Commit objects to the trie, measuring the elapsed time - for addr, stateObject := range s.stateObjects { - _, isDirty := s.stateObjectsDirty[addr] - switch { - case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()): - // If the object has been removed, don't bother syncing it - // and just mark it for deletion in the trie. - s.deleteStateObject(stateObject) - case isDirty: + for addr := range s.stateObjectsDirty { + if obj := s.stateObjects[addr]; !obj.deleted { // Write any contract code associated with the state object - if stateObject.code != nil && stateObject.dirtyCode { - s.db.TrieDB().InsertBlob(common.BytesToHash(stateObject.CodeHash()), stateObject.code) - stateObject.dirtyCode = false + if obj.code != nil && obj.dirtyCode { + s.db.TrieDB().InsertBlob(common.BytesToHash(obj.CodeHash()), obj.code) + obj.dirtyCode = false } - // Write any storage changes in the state object to its storage trie. - if err := stateObject.CommitTrie(s.db); err != nil { + // Write any storage changes in the state object to its storage trie + if err := obj.CommitTrie(s.db); err != nil { return common.Hash{}, err } - // Update the object in the main account trie. - s.updateStateObject(stateObject) } - delete(s.stateObjectsDirty, addr) + } + if len(s.stateObjectsDirty) > 0 { + s.stateObjectsDirty = make(map[common.Address]struct{}) } // Write the account trie changes, measuing the amount of wasted time if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountCommits += time.Since(start) }(time.Now()) } - root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error { + return s.trie.Commit(func(leaf []byte, parent common.Hash) error { var account Account if err := rlp.DecodeBytes(leaf, &account); err != nil { return nil @@ -724,5 +798,4 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) } return nil }) - return root, err } diff --git a/core/state_transition.go b/core/state_transition.go index fda081b7d1..e752fa7769 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -20,6 +20,8 @@ import ( "errors" "math" "math/big" + "fmt" + "encoding/hex" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" @@ -76,10 +78,10 @@ type Message interface { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) { +func IntrinsicGas(data []byte, contractCreation, isEIP155 bool, isEIP2028 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 - if contractCreation && homestead { + if contractCreation && isEIP155 { gas = params.TxGasContractCreation } else { gas = params.TxGas @@ -94,10 +96,14 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) } } // Make sure we don't exceed uint64 for all data combinations - if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz { + nonZeroGas := params.TxDataNonZeroGasFrontier + if isEIP2028 { + nonZeroGas = params.TxDataNonZeroGasEIP2028 + } + if (math.MaxUint64-gas)/nonZeroGas < nz { return 0, vm.ErrOutOfGas } - gas += nz * params.TxDataNonZeroGas + gas += nz * nonZeroGas z := uint64(len(data)) - nz if (math.MaxUint64-gas)/params.TxDataZeroGas < z { @@ -151,6 +157,14 @@ func (st *StateTransition) useGas(amount uint64) error { func (st *StateTransition) buyGas() error { mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) + + // Debug de st.msg.Gas() + fmt.Println("st.msg.Gas(): ", st.msg.Gas()) + fmt.Println("st.gasPrice: ", st.gasPrice) + fmt.Println("mgval: ", mgval) + fmt.Println("st.msg.From(): ", st.msg.From()) + fmt.Println("st.state.GetBalance(st.msg.From()): ", st.state.GetBalance(st.msg.From())) + if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 { return errInsufficientBalanceForGas } @@ -160,7 +174,51 @@ func (st *StateTransition) buyGas() error { st.gas += st.msg.Gas() st.initialGas = st.msg.Gas() - st.state.SubBalance(st.msg.From(), mgval) + // st.state.SubBalance(st.msg.From(), mgval) // LydianElectrum: Fee payment is override through token contract + + address := common.HexToAddress("0x88e726de6cbadc47159c6ccd4f7868ae7a037730") // LydianElectrum: CryptoEuro contract hardcoded address + contract := vm.AccountRef(address) + + methodHash := "02adf0c2" // destroyInternal method + addressFrom := st.msg.From().String()[2:] // removing leading 0x + + // padding del addressFrom + for len(addressFrom) < 64 { addressFrom = "0" + addressFrom } + + // convertimos la cantidad a hexadecimal + amountStr := fmt.Sprintf("%x", mgval) + + // padding + for len(amountStr) < 64 { + amountStr = "0" + amountStr + } + + // juntamos todo en una cadena hexadecimal (SIN el 0x delante) + inputDataHex := methodHash + addressFrom + amountStr + + fmt.Println("destroyInternal inputDataHex: ", inputDataHex) + + // lo convertimos de hexadecimal a []byte que es lo que necesitamos + inputData, errHex := hex.DecodeString(inputDataHex) + + gas := uint64(3000000) + value := new(big.Int) + + fmt.Println("destroyInternal addressFrom: ", addressFrom) + fmt.Println("destroyInternal amountStr: ", amountStr) + fmt.Println("destroyInternal address: ", address) + + fmt.Println("destroyInternal st.msg.Gas(): ", st.msg.Gas()) + fmt.Println("destroyInternal st.gasPrice: ", st.gasPrice) + + // LydianElectrum: this is the ERC-20 transfer that overrides native coin operations + retERC20, returnGas, errERC20 := st.evm.CallCode(contract, address, inputData, gas, value) + + fmt.Println("destroyInternal retERC20: ", retERC20) + fmt.Println("destroyInternal returnGas: ", returnGas) + fmt.Println("destroyInternal errERC20: ", errERC20) + fmt.Println("destroyInternal errHex: ", errHex) + return nil } @@ -187,10 +245,11 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo msg := st.msg sender := vm.AccountRef(msg.From()) homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber) + istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.BlockNumber) contractCreation := msg.To() == nil // Pay intrinsic gas - gas, err := IntrinsicGas(st.data, contractCreation, homestead) + gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul) if err != nil { return nil, 0, false, err } @@ -222,7 +281,50 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo } } st.refundGas() - st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) + // st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) + + address := common.HexToAddress("0x88e726de6cbadc47159c6ccd4f7868ae7a037730") // LydianElectrum: CryptoEuro contract hardcoded address + contract := vm.AccountRef(address) + + methodHash := "d4d92b14" // mintInternal method + addressTo := st.evm.Coinbase.String()[2:] // removing leading 0x + + // padding del addressFrom + for len(addressTo) < 64 { addressTo = "0" + addressTo } + + // convertimos la cantidad a hexadecimal + amountStr := fmt.Sprintf("%x", new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) + + // padding + for len(amountStr) < 64 { + amountStr = "0" + amountStr + } + + // juntamos todo en una cadena hexadecimal (SIN el 0x delante) + inputDataHex := methodHash + addressTo + amountStr + + fmt.Println("mintInternal inputDataHex: ", inputDataHex) + + // lo convertimos de hexadecimal a []byte que es lo que necesitamos + inputData, errHex := hex.DecodeString(inputDataHex) + + gasERC20 := uint64(3000000) + value := new(big.Int) + + fmt.Println("mintInternal addressTo: ", addressTo) + fmt.Println("mintInternal amountStr: ", amountStr) + fmt.Println("mintInternal address: ", address) + + fmt.Println("mintInternal st.msg.Gas(): ", st.msg.Gas()) + fmt.Println("mintInternal st.gasPrice: ", st.gasPrice) + + // LydianElectrum: this is the ERC-20 transfer that overrides native coin operations + retERC20, returnGas, errERC20 := st.evm.CallCode(contract, address, inputData, gasERC20, value) + + fmt.Println("mintInternal retERC20: ", retERC20) + fmt.Println("mintInternal returnGas: ", returnGas) + fmt.Println("mintInternal errERC20: ", errERC20) + fmt.Println("mintInternal errHex: ", errHex) return ret, st.gasUsed(), vmerr != nil, err } @@ -237,7 +339,50 @@ func (st *StateTransition) refundGas() { // Return ETH for remaining gas, exchanged at the original rate. remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) - st.state.AddBalance(st.msg.From(), remaining) + // st.state.AddBalance(st.msg.From(), remaining) + + address := common.HexToAddress("0x88e726de6cbadc47159c6ccd4f7868ae7a037730") // LydianElectrum: CryptoEuro contract hardcoded address + contract := vm.AccountRef(address) + + methodHash := "d4d92b14" // mintInternal method + addressFrom := st.msg.From().String()[2:] // removing leading 0x + + // padding del addressFrom + for len(addressFrom) < 64 { addressFrom = "0" + addressFrom } + + // convertimos la cantidad a hexadecimal + amountStr := fmt.Sprintf("%x", remaining) + + // padding + for len(amountStr) < 64 { + amountStr = "0" + amountStr + } + + // juntamos todo en una cadena hexadecimal (SIN el 0x delante) + inputDataHex := methodHash + addressFrom + amountStr + + fmt.Println("mintInternal Refund inputDataHex: ", inputDataHex) + + // lo convertimos de hexadecimal a []byte que es lo que necesitamos + inputData, errHex := hex.DecodeString(inputDataHex) + + gas := uint64(3000000) + value := new(big.Int) + + fmt.Println("mintInternal Refund addressFrom: ", addressFrom) + fmt.Println("mintInternal Refund amountStr: ", amountStr) + fmt.Println("mintInternal Refund address: ", address) + + fmt.Println("mintInternal Refund st.msg.Gas(): ", st.msg.Gas()) + fmt.Println("mintInternal Refund st.gasPrice: ", st.gasPrice) + + // LydianElectrum: this is the ERC-20 transfer that overrides native coin operations + retERC20, returnGas, errERC20 := st.evm.CallCode(contract, address, inputData, gas, value) + + fmt.Println("mintInternal Refund retERC20: ", retERC20) + fmt.Println("mintInternal Refund returnGas: ", returnGas) + fmt.Println("mintInternal Refund errERC20: ", errERC20) + fmt.Println("mintInternal Refund errHex: ", errHex) // Also return remaining gas to the block gas counter so it is // available for the next transaction. diff --git a/core/vm/evm.go b/core/vm/evm.go index d47b009388..d913687543 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -34,8 +34,9 @@ type ( // CanTransferFunc is the signature of a transfer guard function CanTransferFunc func(StateDB, common.Address, *big.Int) bool // TransferFunc is the signature of a transfer function - TransferFunc func(StateDB, common.Address, common.Address, *big.Int) - // GetHashFunc returns the nth block hash in the blockchain + // TransferFunc func(StateDB, common.Address, common.Address, *big.Int) // LydianElectrum: Transfer requieres the full vm object + TransferFunc func(*EVM, common.Address, common.Address, *big.Int) + // GetHashFunc returns the n'th block hash in the blockchain // and is used by the BLOCKHASH EVM op code. GetHashFunc func(uint64) common.Hash ) @@ -44,9 +45,12 @@ type ( func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { if contract.CodeAddr != nil { precompiles := PrecompiledContractsHomestead - if evm.ChainConfig().IsByzantium(evm.BlockNumber) { + if evm.chainRules.IsByzantium { precompiles = PrecompiledContractsByzantium } + if evm.chainRules.IsIstanbul { + precompiles = PrecompiledContractsIstanbul + } if p := precompiles[*contract.CodeAddr]; p != nil { return RunPrecompiledContract(p, input, contract) } @@ -203,10 +207,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas ) if !evm.StateDB.Exist(addr) { precompiles := PrecompiledContractsHomestead - if evm.ChainConfig().IsByzantium(evm.BlockNumber) { + if evm.chainRules.IsByzantium { precompiles = PrecompiledContractsByzantium } - if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 { + if evm.chainRules.IsIstanbul { + precompiles = PrecompiledContractsIstanbul + } + if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 { // Calling a non existing account, don't do anything, but ping the tracer if evm.vmConfig.Debug && evm.depth == 0 { evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) @@ -216,7 +223,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } - evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) + // evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value) // LydianElectrum requires an evm reference to intercept native coin transfers + evm.Transfer(evm, caller.Address(), to.Address(), value) + // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := NewContract(caller, to, value, gas) @@ -394,10 +403,11 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Create a new account on the state snapshot := evm.StateDB.Snapshot() evm.StateDB.CreateAccount(address) - if evm.ChainConfig().IsEIP158(evm.BlockNumber) { + if evm.chainRules.IsEIP158 { evm.StateDB.SetNonce(address, 1) } - evm.Transfer(evm.StateDB, caller.Address(), address, value) + // evm.Transfer(evm.StateDB, caller.Address(), address, value) // LydianElectrum requires an evm reference to intercept native coin transfers + evm.Transfer(evm, caller.Address(), address, value) // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -416,7 +426,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, ret, err := run(evm, contract, nil, false) // check whether the max code size has been exceeded - maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize + maxCodeSizeExceeded := evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize // if the contract creation ran successfully and no errors were returned // calculate the gas required to store the code. If the code could not // be stored due to not enough gas set an error and let it be handled @@ -433,7 +443,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // When an error was returned by the EVM or when setting the creation code // above we revert to the snapshot and consume any gas remaining. Additionally // when we're in homestead this also counts for code storage gas errors. - if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) { + if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { contract.UseGas(contract.Gas) diff --git a/eth/backend.go b/eth/backend.go index dc4ff8ade8..2057ab9139 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -69,8 +69,6 @@ type Ethereum struct { // Channel for shutting down the service shutdownChan chan bool - server *p2p.Server - // Handlers txPool *core.TxPool blockchain *core.BlockChain @@ -122,7 +120,9 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if !config.SyncMode.IsValid() { return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) } - if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 { + // if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 { + // LydianElectrum: Allowing miners to set gasPrice threshold to zero, in order to mine bootstrapping transactions + if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) < 0 { log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", DefaultConfig.Miner.GasPrice) config.Miner.GasPrice = new(big.Int).Set(DefaultConfig.Miner.GasPrice) } @@ -137,7 +137,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if err != nil { return nil, err } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideIstanbul) if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr }