add cxReceipt data type; add cxReceipt encode/decode; add logic wip

pull/1357/head
chao 5 years ago
parent bee4ad3002
commit 02fb5cb7b2
  1. 2
      api/proto/node/node_test.go
  2. 4
      api/service/explorer/storage_test.go
  3. 2
      api/service/explorer/structs_test.go
  4. 4
      consensus/consensus_service.go
  5. 2
      consensus/engine/consensus_engine.go
  6. 7
      core/block_validator.go
  7. 10
      core/blockchain.go
  8. 6
      core/chain_makers.go
  9. 14
      core/core_test.go
  10. 2
      core/genesis.go
  11. 35
      core/rawdb/accessors_chain.go
  12. 2
      core/rawdb/accessors_indexes_test.go
  13. 7
      core/rawdb/schema.go
  14. 27
      core/state_processor.go
  15. 5
      core/state_transition.go
  16. 2
      core/tx_pool_test.go
  17. 4
      core/types.go
  18. 36
      core/types/block.go
  19. 33
      core/types/cx_receipt.go
  20. 56
      core/types/transaction.go
  21. 1
      core/types/transaction_signing.go
  22. 2
      drand/drand_test.go
  23. 2
      internal/hmyapi/blockchain.go
  24. 11
      node/worker/worker.go

@ -88,7 +88,7 @@ func TestConstructBlocksSyncMessage(t *testing.T) {
t.Fatalf("statedb.Database().TrieDB().Commit() failed: %s", err) t.Fatalf("statedb.Database().TrieDB().Commit() failed: %s", err)
} }
block1 := types.NewBlock(head, nil, nil) block1 := types.NewBlock(head, nil, nil, nil)
blocks := []*types.Block{ blocks := []*types.Block{
block1, block1,

@ -48,7 +48,7 @@ func TestDump(t *testing.T) {
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3} txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil) block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
ins := GetStorageInstance("1.1.1.1", "3333", true) ins := GetStorageInstance("1.1.1.1", "3333", true)
ins.Dump(block, uint64(1)) ins.Dump(block, uint64(1))
db := ins.GetDB() db := ins.GetDB()
@ -75,7 +75,7 @@ func TestUpdateAddressStorage(t *testing.T) {
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3} txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil) block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
ins := GetStorageInstance("1.1.1.1", "3333", true) ins := GetStorageInstance("1.1.1.1", "3333", true)
ins.Dump(block, uint64(1)) ins.Dump(block, uint64(1))
db := ins.GetDB() db := ins.GetDB()

@ -18,7 +18,7 @@ func TestGetTransaction(t *testing.T) {
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3} txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil) block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
tx := GetTransaction(tx1, block) tx := GetTransaction(tx1, block)
assert.Equal(t, tx.ID, tx1.Hash().Hex(), "should be equal tx1.Hash()") assert.Equal(t, tx.ID, tx1.Hash().Hex(), "should be equal tx1.Hash()")

@ -284,14 +284,14 @@ func (consensus *Consensus) VerifySeal(chain consensus_engine.ChainReader, heade
// Finalize implements consensus.Engine, accumulating the block and uncle rewards, // Finalize implements consensus.Engine, accumulating the block and uncle rewards,
// setting the final state and assembling the block. // setting the final state and assembling the block.
func (consensus *Consensus) Finalize(chain consensus_engine.ChainReader, header *types.Header, state *state.DB, txs []*types.Transaction, receipts []*types.Receipt) (*types.Block, error) { func (consensus *Consensus) Finalize(chain consensus_engine.ChainReader, header *types.Header, state *state.DB, txs []*types.Transaction, receipts []*types.Receipt, cxreceipts []*types.CXReceipt) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root // Accumulate any block and uncle rewards and commit the final state root
// Header seems complete, assemble into a block and return // Header seems complete, assemble into a block and return
if err := accumulateRewards(chain, state, header); err != nil { if err := accumulateRewards(chain, state, header); err != nil {
return nil, ctxerror.New("cannot pay block reward").WithCause(err) return nil, ctxerror.New("cannot pay block reward").WithCause(err)
} }
header.Root = state.IntermediateRoot(false) header.Root = state.IntermediateRoot(false)
return types.NewBlock(header, txs, receipts), nil return types.NewBlock(header, txs, receipts, cxreceipts), nil
} }
// Sign on the hash of the message // Sign on the hash of the message

@ -67,7 +67,7 @@ type Engine interface {
// Note: The block header and state database might be updated to reflect any // Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards). // consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainReader, header *types.Header, state *state.DB, txs []*types.Transaction, Finalize(chain ChainReader, header *types.Header, state *state.DB, txs []*types.Transaction,
receipts []*types.Receipt) (*types.Block, error) receipts []*types.Receipt, cxs []*types.CXReceipt) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes // Seal generates a new sealing request for the given input block and pushes
// the result into the given channel. // the result into the given channel.

@ -74,7 +74,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
// transition, such as amount of used gas, the receipt roots and the state root // transition, such as amount of used gas, the receipt roots and the state root
// itself. ValidateState returns a database batch if the validation was a success // itself. ValidateState returns a database batch if the validation was a success
// otherwise nil and an error is returned. // otherwise nil and an error is returned.
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.DB, receipts types.Receipts, usedGas uint64) error { func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.DB, receipts types.Receipts, cxReceipts types.CXReceipts, usedGas uint64) error {
header := block.Header() header := block.Header()
if block.GasUsed() != usedGas { if block.GasUsed() != usedGas {
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
@ -90,6 +90,11 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
if receiptSha != header.ReceiptHash { if receiptSha != header.ReceiptHash {
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
} }
cxsSha := types.DeriveSha(cxReceipts)
if cxsSha != header.CXReceiptHash {
return fmt.Errorf("invalid cross shard receipt root hash (remote: %x local: %x)", header.CXReceiptHash, cxsSha)
}
// Validate the state root against the received state root and throw // Validate the state root against the received state root and throw
// an error if they don't match. // an error if they don't match.
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {

@ -217,13 +217,13 @@ func (bc *BlockChain) ValidateNewBlock(block *types.Block) error {
} }
// Process block using the parent state as reference point. // Process block using the parent state as reference point.
receipts, _, usedGas, err := bc.processor.Process(block, state, bc.vmConfig) receipts, cxReceipts, _, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
if err != nil { if err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
return err return err
} }
err = bc.Validator().ValidateState(block, bc.CurrentBlock(), state, receipts, usedGas) err = bc.Validator().ValidateState(block, bc.CurrentBlock(), state, receipts, cxReceipts, usedGas)
if err != nil { if err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
return err return err
@ -1259,13 +1259,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
return i, events, coalescedLogs, err return i, events, coalescedLogs, err
} }
// Process block using the parent state as reference point. // Process block using the parent state as reference point.
receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig) receipts, cxReceipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
if err != nil { if err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
return i, events, coalescedLogs, err return i, events, coalescedLogs, err
} }
// Validate the state using the default validator // Validate the state using the default validator
err = bc.Validator().ValidateState(block, parent, state, receipts, usedGas) err = bc.Validator().ValidateState(block, parent, state, receipts, cxReceipts, usedGas)
if err != nil { if err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
return i, events, coalescedLogs, err return i, events, coalescedLogs, err
@ -1968,7 +1968,7 @@ func (bc *BlockChain) WriteCrossLinks(cls []types.CrossLink) error {
return err return err
} }
// ReadCrossLink retrieves crosslink hash given shardID and blockNum // ReadCrossLinkHash retrieves crosslink hash given shardID and blockNum
func (bc *BlockChain) ReadCrossLinkHash(shardID uint32, blockNum uint64) (common.Hash, error) { func (bc *BlockChain) ReadCrossLinkHash(shardID uint32, blockNum uint64) (common.Hash, error) {
h, err := rawdb.ReadCrossLinkShardBlock(bc.db, shardID, blockNum) h, err := rawdb.ReadCrossLinkShardBlock(bc.db, shardID, blockNum)
if err != nil { if err != nil {

@ -96,7 +96,8 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
b.SetCoinbase(common.Address{}) b.SetCoinbase(common.Address{})
} }
b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
receipt, _, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) // TODO (chao): may need to add cxReceipt for BlockGen
receipt, _, _, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -185,7 +186,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
} }
if b.engine != nil { if b.engine != nil {
// Finalize and seal the block // Finalize and seal the block
block, err := b.engine.Finalize(chainreader, b.header, statedb, b.txs, b.receipts) // TODO (chao): add cxReceipt in the last input
block, err := b.engine.Finalize(chainreader, b.header, statedb, b.txs, b.receipts, nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }

@ -9,13 +9,13 @@ import (
) )
func TestIsEpochBlock(t *testing.T) { func TestIsEpochBlock(t *testing.T) {
block1 := types.NewBlock(&types.Header{Number: big.NewInt(10)}, nil, nil) block1 := types.NewBlock(&types.Header{Number: big.NewInt(10)}, nil, nil, nil)
block2 := types.NewBlock(&types.Header{Number: big.NewInt(0)}, nil, nil) block2 := types.NewBlock(&types.Header{Number: big.NewInt(0)}, nil, nil, nil)
block3 := types.NewBlock(&types.Header{Number: big.NewInt(344064)}, nil, nil) block3 := types.NewBlock(&types.Header{Number: big.NewInt(344064)}, nil, nil, nil)
block4 := types.NewBlock(&types.Header{Number: big.NewInt(77)}, nil, nil) block4 := types.NewBlock(&types.Header{Number: big.NewInt(77)}, nil, nil, nil)
block5 := types.NewBlock(&types.Header{Number: big.NewInt(78)}, nil, nil) block5 := types.NewBlock(&types.Header{Number: big.NewInt(78)}, nil, nil, nil)
block6 := types.NewBlock(&types.Header{Number: big.NewInt(188)}, nil, nil) block6 := types.NewBlock(&types.Header{Number: big.NewInt(188)}, nil, nil, nil)
block7 := types.NewBlock(&types.Header{Number: big.NewInt(189)}, nil, nil) block7 := types.NewBlock(&types.Header{Number: big.NewInt(189)}, nil, nil, nil)
tests := []struct { tests := []struct {
schedule shardingconfig.Schedule schedule shardingconfig.Schedule
block *types.Block block *types.Block

@ -261,7 +261,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
statedb.Commit(false) statedb.Commit(false)
statedb.Database().TrieDB().Commit(root, true) statedb.Database().TrieDB().Commit(root, true)
return types.NewBlock(head, nil, nil) return types.NewBlock(head, nil, nil, nil)
} }
// Commit writes the block and state of a genesis specification to the database. // Commit writes the block and state of a genesis specification to the database.

@ -512,3 +512,38 @@ func ReadCrossLinkShardBlock(db DatabaseReader, shardID uint32, blockNum uint64)
func WriteCrossLinkShardBlock(db DatabaseWriter, shardID uint32, blockNum uint64, data []byte) error { func WriteCrossLinkShardBlock(db DatabaseWriter, shardID uint32, blockNum uint64, data []byte) error {
return db.Put(crosslinkKey(shardID, blockNum), data) return db.Put(crosslinkKey(shardID, blockNum), data)
} }
// ReadCXReceipts retrieves all the transaction receipts belonging to a block.
func ReadCXReceipts(db DatabaseReader, hash common.Hash, number uint64) types.CXReceipts {
// Retrieve the flattened receipt slice
data, _ := db.Get(cxReceiptKey(number, hash))
if len(data) == 0 {
return nil
}
// Convert the cross shard tx receipts from their storage form to their internal representation
cxReceipts := types.CXReceipts{}
if err := rlp.DecodeBytes(data, &cxReceipts); err != nil {
utils.Logger().Error().Err(err).Str("hash", hash.Hex()).Msg("Invalid cross-shard tx receipt array RLP")
return nil
}
return cxReceipts
}
// WriteCXReceipts stores all the transaction receipts belonging to a block.
func WriteCXReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.CXReceipts) {
bytes, err := rlp.EncodeToBytes(receipts)
if err != nil {
utils.Logger().Error().Msg("Failed to encode cross shard tx receipts")
}
// Store the receipt slice
if err := db.Put(cxReceiptKey(number, hash), bytes); err != nil {
utils.Logger().Error().Msg("Failed to store block receipts")
}
}
// DeleteCXReceipts removes all receipt data associated with a block hash.
func DeleteCXReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
if err := db.Delete(cxReceiptKey(number, hash)); err != nil {
utils.Logger().Error().Msg("Failed to delete cross shard tx receipts")
}
}

@ -34,7 +34,7 @@ func TestLookupStorage(t *testing.T) {
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), 0, big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3} txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil) block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
// Check that no transactions entries are in a pristine database // Check that no transactions entries are in a pristine database
for i, tx := range txs { for i, tx := range txs {

@ -62,6 +62,8 @@ var (
crosslinkPrefix = []byte("crosslink") // prefix for crosslink crosslinkPrefix = []byte("crosslink") // prefix for crosslink
cxReceiptPrefix = []byte("cxReceipt") // prefix for cross shard transaction receipt
// epochBlockNumberPrefix + epoch (big.Int.Bytes()) // epochBlockNumberPrefix + epoch (big.Int.Bytes())
// -> epoch block number (big.Int.Bytes()) // -> epoch block number (big.Int.Bytes())
epochBlockNumberPrefix = []byte("harmony-epoch-block-number-") epochBlockNumberPrefix = []byte("harmony-epoch-block-number-")
@ -172,3 +174,8 @@ func crosslinkKey(shardID uint32, blockNum uint64) []byte {
key := append(crosslinkPrefix, sbKey...) key := append(crosslinkPrefix, sbKey...)
return key return key
} }
// cxReceiptKey = cxReceiptsPrefix + num (uint64 big endian) + hash
func cxReceiptKey(number uint64, hash common.Hash) []byte {
return append(append(cxReceiptPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
}

@ -53,46 +53,44 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen
// Process returns the receipts and logs accumulated during the process and // Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the // returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error. // transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.DB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { func (p *StateProcessor) Process(block *types.Block, statedb *state.DB, cfg vm.Config) (types.Receipts, types.CXReceipts, []*types.Log, uint64, error) {
var ( var (
receipts types.Receipts receipts types.Receipts
cxs types.CXReceipts
usedGas = new(uint64) usedGas = new(uint64)
header = block.Header() header = block.Header()
coinbase = block.Header().Coinbase coinbase = block.Header().Coinbase
allLogs []*types.Log allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit()) gp = new(GasPool).AddGas(block.GasLimit())
) )
// Mutate the block and state according to any hard-fork specs
//if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
// misc.ApplyDAOHardFork(statedb)
//}
// Iterate over and process the individual transactions // Iterate over and process the individual transactions
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
statedb.Prepare(tx.Hash(), block.Hash(), i) statedb.Prepare(tx.Hash(), block.Hash(), i)
receipt, _, err := ApplyTransaction(p.config, p.bc, &coinbase, gp, statedb, header, tx, usedGas, cfg) receipt, cxReceipt, _, err := ApplyTransaction(p.config, p.bc, &coinbase, gp, statedb, header, tx, usedGas, cfg)
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, nil, 0, err
} }
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
cxs = append(cxs, cxReceipt)
allLogs = append(allLogs, receipt.Logs...) allLogs = append(allLogs, receipt.Logs...)
} }
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards) // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
_, err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), receipts) _, err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), receipts, cxs)
if err != nil { if err != nil {
return nil, nil, 0, ctxerror.New("cannot finalize block").WithCause(err) return nil, nil, nil, 0, ctxerror.New("cannot finalize block").WithCause(err)
} }
return receipts, allLogs, *usedGas, nil return receipts, cxs, allLogs, *usedGas, nil
} }
// ApplyTransaction attempts to apply a transaction to the given state database // ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. It returns the receipt // and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed, // for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid. // indicating the block was invalid.
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.DB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) { func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.DB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, *types.CXReceipt, uint64, error) {
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
if err != nil { if err != nil {
return nil, 0, err return nil, nil, 0, err
} }
// Create a new context to be used in the EVM environment // Create a new context to be used in the EVM environment
context := NewEVMContext(msg, header, bc, author) context := NewEVMContext(msg, header, bc, author)
@ -102,7 +100,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
// Apply the transaction to the current state (included in the env) // Apply the transaction to the current state (included in the env)
_, gas, failed, err := ApplyMessage(vmenv, msg, gp) _, gas, failed, err := ApplyMessage(vmenv, msg, gp)
if err != nil { if err != nil {
return nil, 0, err return nil, nil, 0, err
} }
// Update the state with pending changes // Update the state with pending changes
var root []byte var root []byte
@ -126,5 +124,6 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
//receipt.Logs = statedb.GetLogs(tx.Hash()) //receipt.Logs = statedb.GetLogs(tx.Hash())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
return receipt, gas, err // TODO (chao): add logic for CXReceipt
return receipt, nil, gas, err
} }

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/core/vm" "github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/internal/utils"
) )
@ -59,6 +60,7 @@ type StateTransition struct {
data []byte data []byte
state vm.StateDB state vm.StateDB
evm *vm.EVM evm *vm.EVM
txType types.TransactionType
} }
// Message represents a message sent to a contract. // Message represents a message sent to a contract.
@ -74,6 +76,8 @@ type Message interface {
Nonce() uint64 Nonce() uint64
CheckNonce() bool CheckNonce() bool
Data() []byte Data() []byte
TxType() types.TransactionType
} }
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. // IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
@ -119,6 +123,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
value: msg.Value(), value: msg.Value(),
data: msg.Data(), data: msg.Data(),
state: evm.StateDB, state: evm.StateDB,
txType: msg.TxType(),
} }
} }

@ -54,7 +54,7 @@ type testBlockChain struct {
func (bc *testBlockChain) CurrentBlock() *types.Block { func (bc *testBlockChain) CurrentBlock() *types.Block {
return types.NewBlock(&types.Header{ return types.NewBlock(&types.Header{
GasLimit: bc.gasLimit, GasLimit: bc.gasLimit,
}, nil, nil) }, nil, nil, nil)
} }
func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {

@ -32,7 +32,7 @@ type Validator interface {
// ValidateState validates the given statedb and optionally the receipts and // ValidateState validates the given statedb and optionally the receipts and
// gas used. // gas used.
ValidateState(block, parent *types.Block, state *state.DB, receipts types.Receipts, usedGas uint64) error ValidateState(block, parent *types.Block, state *state.DB, receipts types.Receipts, cxs types.CXReceipts, usedGas uint64) error
} }
// Processor is an interface for processing blocks using a given initial state. // Processor is an interface for processing blocks using a given initial state.
@ -42,5 +42,5 @@ type Validator interface {
// of gas used in the process and return an error if any of the internal rules // of gas used in the process and return an error if any of the internal rules
// failed. // failed.
type Processor interface { type Processor interface {
Process(block *types.Block, statedb *state.DB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) Process(block *types.Block, statedb *state.DB, cfg vm.Config) (types.Receipts, types.CXReceipts, []*types.Log, uint64, error)
} }

@ -71,18 +71,19 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
// Header represents a block header in the Harmony blockchain. // Header represents a block header in the Harmony blockchain.
type Header struct { type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"` ParentHash common.Hash `json:"parentHash" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"` Coinbase common.Address `json:"miner" gencodec:"required"`
Root common.Hash `json:"stateRoot" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom ethtypes.Bloom `json:"logsBloom" gencodec:"required"` CXReceiptHash common.Hash `json:"cxReceiptsRoot" gencodec:"required"`
Number *big.Int `json:"number" gencodec:"required"` Bloom ethtypes.Bloom `json:"logsBloom" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"` Number *big.Int `json:"number" gencodec:"required"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"` GasLimit uint64 `json:"gasLimit" gencodec:"required"`
Time *big.Int `json:"timestamp" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Extra []byte `json:"extraData" gencodec:"required"` Time *big.Int `json:"timestamp" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
// Additional Fields // Additional Fields
ViewID *big.Int `json:"viewID" gencodec:"required"` ViewID *big.Int `json:"viewID" gencodec:"required"`
Epoch *big.Int `json:"epoch" gencodec:"required"` Epoch *big.Int `json:"epoch" gencodec:"required"`
@ -225,7 +226,7 @@ type storageblock struct {
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header // The values of TxHash, UncleHash, ReceiptHash and Bloom in header
// are ignored and set to values derived from the given txs, // are ignored and set to values derived from the given txs,
// and receipts. // and receipts.
func NewBlock(header *Header, txs []*Transaction, receipts []*Receipt) *Block { func NewBlock(header *Header, txs []*Transaction, receipts []*Receipt, cxs []*CXReceipt) *Block {
b := &Block{header: CopyHeader(header)} b := &Block{header: CopyHeader(header)}
// TODO: panic if len(txs) != len(receipts) // TODO: panic if len(txs) != len(receipts)
@ -244,6 +245,12 @@ func NewBlock(header *Header, txs []*Transaction, receipts []*Receipt) *Block {
b.header.Bloom = CreateBloom(receipts) b.header.Bloom = CreateBloom(receipts)
} }
if len(cxs) == 0 {
b.header.CXReceiptHash = EmptyRootHash
} else {
b.header.CXReceiptHash = DeriveSha(CXReceipts(cxs))
}
return b return b
} }
@ -385,6 +392,9 @@ func (b *Block) TxHash() common.Hash { return b.header.TxHash }
// ReceiptHash returns header receipt hash. // ReceiptHash returns header receipt hash.
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash }
// CXReceiptHash returns header cross shard receipt hash.
func (b *Block) CXReceiptHash() common.Hash { return b.header.CXReceiptHash }
// Extra returns header extra. // Extra returns header extra.
func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) }

@ -0,0 +1,33 @@
package types
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
// CXReceipt represents a receipt for cross-shard transaction
type CXReceipt struct {
Nonce uint64
From common.Address
To common.Address
ShardID uint32
ToShardID uint32
Amount *big.Int
}
// CXReceipts is a list of CXReceipt
type CXReceipts []*CXReceipt
// Len returns the length of s.
func (cs CXReceipts) Len() int { return len(cs) }
// Swap swaps the i'th and the j'th element in s.
func (cs CXReceipts) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
// GetRlp implements Rlpable and returns the i'th element of s in rlp.
func (cs CXReceipts) GetRlp(i int) []byte {
enc, _ := rlp.EncodeToBytes(cs[i])
return enc
}

@ -36,6 +36,15 @@ var (
ErrInvalidSig = errors.New("invalid transaction v, r, s values") ErrInvalidSig = errors.New("invalid transaction v, r, s values")
) )
// TransactionType different types of transactions
type TransactionType byte
const (
SameShardTx TransactionType = iota
SubtractionOnly // only subtract tokens from source shard account
AdditionOnly // only add tokens to destination shard account
)
// Transaction struct. // Transaction struct.
type Transaction struct { type Transaction struct {
data txdata data txdata
@ -45,16 +54,30 @@ type Transaction struct {
from atomic.Value from atomic.Value
} }
//String print mode string
func (txType TransactionType) String() string {
if txType == SameShardTx {
return "SameShardTx"
} else if txType == SubtractionOnly {
return "SubtractionOnly"
} else if txType == AdditionOnly {
return "AdditionOnly"
}
return "Unknown"
}
type txdata struct { type txdata struct {
AccountNonce uint64 `json:"nonce" gencodec:"required"` AccountNonce uint64 `json:"nonce" gencodec:"required"`
Price *big.Int `json:"gasPrice" gencodec:"required"` Price *big.Int `json:"gasPrice" gencodec:"required"`
GasLimit uint64 `json:"gas" gencodec:"required"` GasLimit uint64 `json:"gas" gencodec:"required"`
ShardID uint32 `json:"shardID" gencodec:"required"` ShardID uint32 `json:"shardID" gencodec:"required"`
ToShardID uint32 `json:"toShardID"` ToShardID uint32 `json:"toShardID" gencodec:"required"`
Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
Amount *big.Int `json:"value" gencodec:"required"` Amount *big.Int `json:"value" gencodec:"required"`
Payload []byte `json:"input" gencodec:"required"` Payload []byte `json:"input" gencodec:"required"`
TxType TransactionType `json:"transactionType" gencodec:"required"`
// Signature values // Signature values
V *big.Int `json:"v" gencodec:"required"` V *big.Int `json:"v" gencodec:"required"`
R *big.Int `json:"r" gencodec:"required"` R *big.Int `json:"r" gencodec:"required"`
@ -73,14 +96,20 @@ type txdataMarshaling struct {
V *hexutil.Big V *hexutil.Big
R *hexutil.Big R *hexutil.Big
S *hexutil.Big S *hexutil.Big
// TODO: add ShardID, ToShardID, TxType ?
} }
// NewTransaction returns new transaction. // NewTransaction returns new transaction, this method is to create same shard transaction
func NewTransaction(nonce uint64, to common.Address, shardID uint32, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { func NewTransaction(nonce uint64, to common.Address, shardID uint32, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
return newTransaction(nonce, &to, shardID, amount, gasLimit, gasPrice, data) return newTransaction(nonce, &to, shardID, amount, gasLimit, gasPrice, data)
} }
// NewContractCreation returns contract transaction. // NewCrossShardTransaction returns new cross shard transaction
func NewCrossShardTransaction(nonce uint64, to common.Address, shardID uint32, toShardID uint32, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, txType TransactionType) *Transaction {
return newCrossShardTransaction(nonce, &to, shardID, toShardID, amount, gasLimit, gasPrice, data, txType)
}
// NewContractCreation returns same shard contract transaction.
func NewContractCreation(nonce uint64, shardID uint32, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { func NewContractCreation(nonce uint64, shardID uint32, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
return newTransaction(nonce, nil, shardID, amount, gasLimit, gasPrice, data) return newTransaction(nonce, nil, shardID, amount, gasLimit, gasPrice, data)
} }
@ -93,7 +122,9 @@ func newTransaction(nonce uint64, to *common.Address, shardID uint32, amount *bi
AccountNonce: nonce, AccountNonce: nonce,
Recipient: to, Recipient: to,
ShardID: shardID, ShardID: shardID,
ToShardID: shardID,
Payload: data, Payload: data,
TxType: SameShardTx,
Amount: new(big.Int), Amount: new(big.Int),
GasLimit: gasLimit, GasLimit: gasLimit,
Price: new(big.Int), Price: new(big.Int),
@ -111,8 +142,7 @@ func newTransaction(nonce uint64, to *common.Address, shardID uint32, amount *bi
return &Transaction{data: d} return &Transaction{data: d}
} }
// create new cross-shard transaction func newCrossShardTransaction(nonce uint64, to *common.Address, shardID uint32, toShardID uint32, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, txType TransactionType) *Transaction {
func newCrossShardTransaction(nonce uint64, to *common.Address, shardID uint32, toShardID uint32, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
if len(data) > 0 { if len(data) > 0 {
data = common.CopyBytes(data) data = common.CopyBytes(data)
} }
@ -122,6 +152,7 @@ func newCrossShardTransaction(nonce uint64, to *common.Address, shardID uint32,
ShardID: shardID, ShardID: shardID,
ToShardID: toShardID, ToShardID: toShardID,
Payload: data, Payload: data,
TxType: txType,
Amount: new(big.Int), Amount: new(big.Int),
GasLimit: gasLimit, GasLimit: gasLimit,
Price: new(big.Int), Price: new(big.Int),
@ -154,6 +185,11 @@ func (tx *Transaction) ToShardID() uint32 {
return tx.data.ToShardID return tx.data.ToShardID
} }
// TxType returns the type of transaction
func (tx *Transaction) TxType() TransactionType {
return tx.data.TxType
}
// Protected returns whether the transaction is protected from replay protection. // Protected returns whether the transaction is protected from replay protection.
func (tx *Transaction) Protected() bool { func (tx *Transaction) Protected() bool {
return isProtectedV(tx.data.V) return isProtectedV(tx.data.V)
@ -293,6 +329,7 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) {
to: tx.data.Recipient, to: tx.data.Recipient,
amount: tx.data.Amount, amount: tx.data.Amount,
data: tx.data.Payload, data: tx.data.Payload,
txType: tx.data.TxType,
checkNonce: true, checkNonce: true,
} }
@ -463,10 +500,11 @@ type Message struct {
gasPrice *big.Int gasPrice *big.Int
data []byte data []byte
checkNonce bool checkNonce bool
txType TransactionType
} }
// NewMessage returns new message. // NewMessage returns new message.
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message { func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool, txType TransactionType) Message {
return Message{ return Message{
from: from, from: from,
to: to, to: to,
@ -476,6 +514,7 @@ func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *b
gasPrice: gasPrice, gasPrice: gasPrice,
data: data, data: data,
checkNonce: checkNonce, checkNonce: checkNonce,
txType: txType,
} }
} }
@ -518,3 +557,8 @@ func (m Message) Data() []byte {
func (m Message) CheckNonce() bool { func (m Message) CheckNonce() bool {
return m.checkNonce return m.checkNonce
} }
// TxType returns the transaction type of the Message
func (m Message) TxType() TransactionType {
return m.txType
}

@ -43,6 +43,7 @@ type sigCache struct {
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
var signer Signer var signer Signer
switch { switch {
// TODO (chao) clean up different forks in ETH code
case config.IsEIP155(blockNumber): case config.IsEIP155(blockNumber):
signer = NewEIP155Signer(config.ChainID) signer = NewEIP155Signer(config.ChainID)
case config.IsHomestead(blockNumber): case config.IsHomestead(blockNumber):

@ -126,7 +126,7 @@ func TestVrf(test *testing.T) {
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
txs := []*types.Transaction{tx1} txs := []*types.Transaction{tx1}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil) block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
blockHash := block.Hash() blockHash := block.Hash()
dRand.vrf(blockHash) dRand.vrf(blockHash)

@ -156,7 +156,7 @@ func doCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumb
} }
// Create new call message // Create new call message
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false) msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false, types.SameShardTx)
// Setup context so it may be cancelled the call has completed // Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout. // or, in case of unmetered gas, setup a context with a timeout.

@ -24,6 +24,7 @@ type environment struct {
header *types.Header header *types.Header
txs []*types.Transaction txs []*types.Transaction
receipts []*types.Receipt receipts []*types.Receipt
cxs []*types.CXReceipt // cross shard transaction receipts (source shard)
} }
// Worker is the main object which takes care of submitting new work to consensus engine // Worker is the main object which takes care of submitting new work to consensus engine
@ -73,13 +74,14 @@ func (w *Worker) SelectTransactionsForNewBlock(txs types.Transactions, maxNumTxs
func (w *Worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { func (w *Worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {
snap := w.current.state.Snapshot() snap := w.current.state.Snapshot()
receipt, _, err := core.ApplyTransaction(w.config, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, vm.Config{}) receipt, cx, _, err := core.ApplyTransaction(w.config, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.header, tx, &w.current.header.GasUsed, vm.Config{})
if err != nil { if err != nil {
w.current.state.RevertToSnapshot(snap) w.current.state.RevertToSnapshot(snap)
return nil, err return nil, err
} }
w.current.txs = append(w.current.txs, tx) w.current.txs = append(w.current.txs, tx)
w.current.receipts = append(w.current.receipts, receipt) w.current.receipts = append(w.current.receipts, receipt)
w.current.cxs = append(w.current.cxs, cx)
return receipt.Logs, nil return receipt.Logs, nil
} }
@ -151,6 +153,11 @@ func (w *Worker) GetCurrentReceipts() []*types.Receipt {
return w.current.receipts return w.current.receipts
} }
// GetCurrentCXReceipts get the receipts generated starting from the last state.
func (w *Worker) GetCurrentCXReceipts() []*types.CXReceipt {
return w.current.cxs
}
// Commit generate a new block for the new txs. // Commit generate a new block for the new txs.
func (w *Worker) Commit(sig []byte, signers []byte, viewID uint64, coinbase common.Address) (*types.Block, error) { func (w *Worker) Commit(sig []byte, signers []byte, viewID uint64, coinbase common.Address) (*types.Block, error) {
if len(sig) > 0 && len(signers) > 0 { if len(sig) > 0 && len(signers) > 0 {
@ -164,7 +171,7 @@ func (w *Worker) Commit(sig []byte, signers []byte, viewID uint64, coinbase comm
s := w.current.state.Copy() s := w.current.state.Copy()
copyHeader := types.CopyHeader(w.current.header) copyHeader := types.CopyHeader(w.current.header)
block, err := w.engine.Finalize(w.chain, copyHeader, s, w.current.txs, w.current.receipts) block, err := w.engine.Finalize(w.chain, copyHeader, s, w.current.txs, w.current.receipts, w.current.cxs)
if err != nil { if err != nil {
return nil, ctxerror.New("cannot finalize block").WithCause(err) return nil, ctxerror.New("cannot finalize block").WithCause(err)
} }

Loading…
Cancel
Save