From d235e2c6a5788ec4a6cff15a16f56b38a3876a0d Mon Sep 17 00:00:00 2001 From: Determinant Date: Sun, 28 Jun 2020 14:47:41 -0400 Subject: ... --- core/types/block.go | 419 ++++++++++++++++++++++++++++++++++++ core/types/bloom9.go | 136 ++++++++++++ core/types/derive_sha.go | 41 ++++ core/types/gen_header_json.go | 138 ++++++++++++ core/types/gen_log_json.go | 92 ++++++++ core/types/gen_receipt_json.go | 104 +++++++++ core/types/gen_tx_json.go | 101 +++++++++ core/types/log.go | 143 +++++++++++++ core/types/receipt.go | 336 +++++++++++++++++++++++++++++ core/types/transaction.go | 440 ++++++++++++++++++++++++++++++++++++++ core/types/transaction_signing.go | 260 ++++++++++++++++++++++ 11 files changed, 2210 insertions(+) create mode 100644 core/types/block.go create mode 100644 core/types/bloom9.go create mode 100644 core/types/derive_sha.go create mode 100644 core/types/gen_header_json.go create mode 100644 core/types/gen_log_json.go create mode 100644 core/types/gen_receipt_json.go create mode 100644 core/types/gen_tx_json.go create mode 100644 core/types/log.go create mode 100644 core/types/receipt.go create mode 100644 core/types/transaction.go create mode 100644 core/types/transaction_signing.go (limited to 'core/types') diff --git a/core/types/block.go b/core/types/block.go new file mode 100644 index 0000000..2fdc904 --- /dev/null +++ b/core/types/block.go @@ -0,0 +1,419 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Package types contains data types related to Ethereum consensus. +package types + +import ( + "encoding/binary" + "fmt" + "io" + "math/big" + "reflect" + "sort" + "sync/atomic" + "time" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" + "github.com/ava-labs/go-ethereum/rlp" + "golang.org/x/crypto/sha3" +) + +var ( + EmptyRootHash = DeriveSha(Transactions{}) + EmptyUncleHash = rlpHash([]*Header(nil)) +) + +// A BlockNonce is a 64-bit hash which proves (combined with the +// mix-hash) that a sufficient amount of computation has been carried +// out on a block. +type BlockNonce [8]byte + +// EncodeNonce converts the given integer to a block nonce. +func EncodeNonce(i uint64) BlockNonce { + var n BlockNonce + binary.BigEndian.PutUint64(n[:], i) + return n +} + +// Uint64 returns the integer value of a block nonce. +func (n BlockNonce) Uint64() uint64 { + return binary.BigEndian.Uint64(n[:]) +} + +// MarshalText encodes n as a hex string with 0x prefix. +func (n BlockNonce) MarshalText() ([]byte, error) { + return hexutil.Bytes(n[:]).MarshalText() +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (n *BlockNonce) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) +} + +//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go + +// Header represents a block header in the Ethereum blockchain. +type Header struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *big.Int `json:"difficulty" gencodec:"required"` + Number *big.Int `json:"number" gencodec:"required"` + GasLimit uint64 `json:"gasLimit" gencodec:"required"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + Time uint64 `json:"timestamp" gencodec:"required"` + Extra []byte `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce BlockNonce `json:"nonce"` +} + +// field type overrides for gencodec +type headerMarshaling struct { + Difficulty *hexutil.Big + Number *hexutil.Big + GasLimit hexutil.Uint64 + GasUsed hexutil.Uint64 + Time hexutil.Uint64 + Extra hexutil.Bytes + Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON +} + +// Hash returns the block hash of the header, which is simply the keccak256 hash of its +// RLP encoding. +func (h *Header) Hash() common.Hash { + return rlpHash(h) +} + +var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size()) + +// Size returns the approximate memory used by all internal contents. It is used +// to approximate and limit the memory consumption of various caches. +func (h *Header) Size() common.StorageSize { + return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen())/8) +} + +// SanityCheck checks a few basic things -- these checks are way beyond what +// any 'sane' production values should hold, and can mainly be used to prevent +// that the unbounded fields are stuffed with junk data to add processing +// overhead +func (h *Header) SanityCheck() error { + if h.Number != nil && !h.Number.IsUint64() { + return fmt.Errorf("too large block number: bitlen %d", h.Number.BitLen()) + } + if h.Difficulty != nil { + if diffLen := h.Difficulty.BitLen(); diffLen > 80 { + return fmt.Errorf("too large block difficulty: bitlen %d", diffLen) + } + } + if eLen := len(h.Extra); eLen > 100*1024 { + return fmt.Errorf("too large block extradata: size %d", eLen) + } + return nil +} + +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewLegacyKeccak256() + rlp.Encode(hw, x) + hw.Sum(h[:0]) + return h +} + +// Body is a simple (mutable, non-safe) data container for storing and moving +// a block's data contents (transactions and uncles) together. +type Body struct { + Transactions []*Transaction + Uncles []*Header +} + +// Block represents an entire block in the Ethereum blockchain. +type Block struct { + header *Header + uncles []*Header + transactions Transactions + + // caches + hash atomic.Value + size atomic.Value + + // Td is used by package core to store the total difficulty + // of the chain up to and including the block. + td *big.Int + + // These fields are used by package eth to track + // inter-peer block relay. + ReceivedAt time.Time + ReceivedFrom interface{} +} + +// DeprecatedTd is an old relic for extracting the TD of a block. It is in the +// code solely to facilitate upgrading the database from the old format to the +// new, after which it should be deleted. Do not use! +func (b *Block) DeprecatedTd() *big.Int { + return b.td +} + +// [deprecated by eth/63] +// StorageBlock defines the RLP encoding of a Block stored in the +// state database. The StorageBlock encoding contains fields that +// would otherwise need to be recomputed. +type StorageBlock Block + +// "external" block encoding. used for eth protocol, etc. +type extblock struct { + Header *Header + Txs []*Transaction + Uncles []*Header +} + +// [deprecated by eth/63] +// "storage" block encoding. used for database. +type storageblock struct { + Header *Header + Txs []*Transaction + Uncles []*Header + TD *big.Int +} + +// NewBlock creates a new block. The input data is copied, +// changes to header and to the field values will not affect the +// block. +// +// The values of TxHash, UncleHash, ReceiptHash and Bloom in header +// are ignored and set to values derived from the given txs, uncles +// and receipts. +func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { + b := &Block{header: CopyHeader(header), td: new(big.Int)} + + // TODO: panic if len(txs) != len(receipts) + if len(txs) == 0 { + b.header.TxHash = EmptyRootHash + } else { + b.header.TxHash = DeriveSha(Transactions(txs)) + b.transactions = make(Transactions, len(txs)) + copy(b.transactions, txs) + } + + if len(receipts) == 0 { + b.header.ReceiptHash = EmptyRootHash + } else { + b.header.ReceiptHash = DeriveSha(Receipts(receipts)) + b.header.Bloom = CreateBloom(receipts) + } + + if len(uncles) == 0 { + b.header.UncleHash = EmptyUncleHash + } else { + b.header.UncleHash = CalcUncleHash(uncles) + b.uncles = make([]*Header, len(uncles)) + for i := range uncles { + b.uncles[i] = CopyHeader(uncles[i]) + } + } + + return b +} + +// NewBlockWithHeader creates a block with the given header data. The +// header data is copied, changes to header and to the field values +// will not affect the block. +func NewBlockWithHeader(header *Header) *Block { + return &Block{header: CopyHeader(header)} +} + +// CopyHeader creates a deep copy of a block header to prevent side effects from +// modifying a header variable. +func CopyHeader(h *Header) *Header { + cpy := *h + if cpy.Difficulty = new(big.Int); h.Difficulty != nil { + cpy.Difficulty.Set(h.Difficulty) + } + if cpy.Number = new(big.Int); h.Number != nil { + cpy.Number.Set(h.Number) + } + if len(h.Extra) > 0 { + cpy.Extra = make([]byte, len(h.Extra)) + copy(cpy.Extra, h.Extra) + } + return &cpy +} + +// DecodeRLP decodes the Ethereum +func (b *Block) DecodeRLP(s *rlp.Stream) error { + var eb extblock + _, size, _ := s.Kind() + if err := s.Decode(&eb); err != nil { + return err + } + b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs + b.size.Store(common.StorageSize(rlp.ListSize(size))) + return nil +} + +// EncodeRLP serializes b into the Ethereum RLP block format. +func (b *Block) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, extblock{ + Header: b.header, + Txs: b.transactions, + Uncles: b.uncles, + }) +} + +// [deprecated by eth/63] +func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error { + var sb storageblock + if err := s.Decode(&sb); err != nil { + return err + } + b.header, b.uncles, b.transactions, b.td = sb.Header, sb.Uncles, sb.Txs, sb.TD + return nil +} + +// TODO: copies + +func (b *Block) Uncles() []*Header { return b.uncles } +func (b *Block) Transactions() Transactions { return b.transactions } + +func (b *Block) Transaction(hash common.Hash) *Transaction { + for _, transaction := range b.transactions { + if transaction.Hash() == hash { + return transaction + } + } + return nil +} + +func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } +func (b *Block) GasLimit() uint64 { return b.header.GasLimit } +func (b *Block) GasUsed() uint64 { return b.header.GasUsed } +func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } +func (b *Block) Time() uint64 { return b.header.Time } + +func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } +func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } +func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } +func (b *Block) Bloom() Bloom { return b.header.Bloom } +func (b *Block) Coinbase() common.Address { return b.header.Coinbase } +func (b *Block) Root() common.Hash { return b.header.Root } +func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } +func (b *Block) TxHash() common.Hash { return b.header.TxHash } +func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } +func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } +func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } + +func (b *Block) Header() *Header { return CopyHeader(b.header) } + +// Body returns the non-header content of the block. +func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } + +// Size returns the true RLP encoded storage size of the block, either by encoding +// and returning it, or returning a previsouly cached value. +func (b *Block) Size() common.StorageSize { + if size := b.size.Load(); size != nil { + return size.(common.StorageSize) + } + c := writeCounter(0) + rlp.Encode(&c, b) + b.size.Store(common.StorageSize(c)) + return common.StorageSize(c) +} + +// SanityCheck can be used to prevent that unbounded fields are +// stuffed with junk data to add processing overhead +func (b *Block) SanityCheck() error { + return b.header.SanityCheck() +} + +type writeCounter common.StorageSize + +func (c *writeCounter) Write(b []byte) (int, error) { + *c += writeCounter(len(b)) + return len(b), nil +} + +func CalcUncleHash(uncles []*Header) common.Hash { + if len(uncles) == 0 { + return EmptyUncleHash + } + return rlpHash(uncles) +} + +// WithSeal returns a new block with the data from b but the header replaced with +// the sealed one. +func (b *Block) WithSeal(header *Header) *Block { + cpy := *header + + return &Block{ + header: &cpy, + transactions: b.transactions, + uncles: b.uncles, + } +} + +// WithBody returns a new block with the given transaction and uncle contents. +func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { + block := &Block{ + header: CopyHeader(b.header), + transactions: make([]*Transaction, len(transactions)), + uncles: make([]*Header, len(uncles)), + } + copy(block.transactions, transactions) + for i := range uncles { + block.uncles[i] = CopyHeader(uncles[i]) + } + return block +} + +// Hash returns the keccak256 hash of b's header. +// The hash is computed on the first call and cached thereafter. +func (b *Block) Hash() common.Hash { + if hash := b.hash.Load(); hash != nil { + return hash.(common.Hash) + } + v := b.header.Hash() + b.hash.Store(v) + return v +} + +type Blocks []*Block + +type BlockBy func(b1, b2 *Block) bool + +func (self BlockBy) Sort(blocks Blocks) { + bs := blockSorter{ + blocks: blocks, + by: self, + } + sort.Sort(bs) +} + +type blockSorter struct { + blocks Blocks + by func(b1, b2 *Block) bool +} + +func (self blockSorter) Len() int { return len(self.blocks) } +func (self blockSorter) Swap(i, j int) { + self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i] +} +func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } + +func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 } diff --git a/core/types/bloom9.go b/core/types/bloom9.go new file mode 100644 index 0000000..76923c4 --- /dev/null +++ b/core/types/bloom9.go @@ -0,0 +1,136 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "fmt" + "math/big" + + "github.com/ava-labs/go-ethereum/common/hexutil" + "github.com/ava-labs/go-ethereum/crypto" +) + +type bytesBacked interface { + Bytes() []byte +} + +const ( + // BloomByteLength represents the number of bytes used in a header log bloom. + BloomByteLength = 256 + + // BloomBitLength represents the number of bits used in a header log bloom. + BloomBitLength = 8 * BloomByteLength +) + +// Bloom represents a 2048 bit bloom filter. +type Bloom [BloomByteLength]byte + +// BytesToBloom converts a byte slice to a bloom filter. +// It panics if b is not of suitable size. +func BytesToBloom(b []byte) Bloom { + var bloom Bloom + bloom.SetBytes(b) + return bloom +} + +// SetBytes sets the content of b to the given bytes. +// It panics if d is not of suitable size. +func (b *Bloom) SetBytes(d []byte) { + if len(b) < len(d) { + panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d))) + } + copy(b[BloomByteLength-len(d):], d) +} + +// Add adds d to the filter. Future calls of Test(d) will return true. +func (b *Bloom) Add(d *big.Int) { + bin := new(big.Int).SetBytes(b[:]) + bin.Or(bin, bloom9(d.Bytes())) + b.SetBytes(bin.Bytes()) +} + +// Big converts b to a big integer. +func (b Bloom) Big() *big.Int { + return new(big.Int).SetBytes(b[:]) +} + +func (b Bloom) Bytes() []byte { + return b[:] +} + +func (b Bloom) Test(test *big.Int) bool { + return BloomLookup(b, test) +} + +func (b Bloom) TestBytes(test []byte) bool { + return b.Test(new(big.Int).SetBytes(test)) + +} + +// MarshalText encodes b as a hex string with 0x prefix. +func (b Bloom) MarshalText() ([]byte, error) { + return hexutil.Bytes(b[:]).MarshalText() +} + +// UnmarshalText b as a hex string with 0x prefix. +func (b *Bloom) UnmarshalText(input []byte) error { + return hexutil.UnmarshalFixedText("Bloom", input, b[:]) +} + +func CreateBloom(receipts Receipts) Bloom { + bin := new(big.Int) + for _, receipt := range receipts { + bin.Or(bin, LogsBloom(receipt.Logs)) + } + + return BytesToBloom(bin.Bytes()) +} + +func LogsBloom(logs []*Log) *big.Int { + bin := new(big.Int) + for _, log := range logs { + bin.Or(bin, bloom9(log.Address.Bytes())) + for _, b := range log.Topics { + bin.Or(bin, bloom9(b[:])) + } + } + + return bin +} + +func bloom9(b []byte) *big.Int { + b = crypto.Keccak256(b) + + r := new(big.Int) + + for i := 0; i < 6; i += 2 { + t := big.NewInt(1) + b := (uint(b[i+1]) + (uint(b[i]) << 8)) & 2047 + r.Or(r, t.Lsh(t, b)) + } + + return r +} + +var Bloom9 = bloom9 + +func BloomLookup(bin Bloom, topic bytesBacked) bool { + bloom := bin.Big() + cmp := bloom9(topic.Bytes()) + + return bloom.And(bloom, cmp).Cmp(cmp) == 0 +} diff --git a/core/types/derive_sha.go b/core/types/derive_sha.go new file mode 100644 index 0000000..8234191 --- /dev/null +++ b/core/types/derive_sha.go @@ -0,0 +1,41 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "bytes" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/rlp" + "github.com/ava-labs/go-ethereum/trie" +) + +type DerivableList interface { + Len() int + GetRlp(i int) []byte +} + +func DeriveSha(list DerivableList) common.Hash { + keybuf := new(bytes.Buffer) + trie := new(trie.Trie) + for i := 0; i < list.Len(); i++ { + keybuf.Reset() + rlp.Encode(keybuf, uint(i)) + trie.Update(keybuf.Bytes(), list.GetRlp(i)) + } + return trie.Hash() +} diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go new file mode 100644 index 0000000..48b35db --- /dev/null +++ b/core/types/gen_header_json.go @@ -0,0 +1,138 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" +) + +var _ = (*headerMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (h Header) MarshalJSON() ([]byte, error) { + type Header struct { + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase common.Address `json:"miner" gencodec:"required"` + Root common.Hash `json:"stateRoot" gencodec:"required"` + TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest common.Hash `json:"mixHash"` + Nonce BlockNonce `json:"nonce"` + Hash common.Hash `json:"hash"` + } + var enc Header + enc.ParentHash = h.ParentHash + enc.UncleHash = h.UncleHash + enc.Coinbase = h.Coinbase + enc.Root = h.Root + enc.TxHash = h.TxHash + enc.ReceiptHash = h.ReceiptHash + enc.Bloom = h.Bloom + enc.Difficulty = (*hexutil.Big)(h.Difficulty) + enc.Number = (*hexutil.Big)(h.Number) + enc.GasLimit = hexutil.Uint64(h.GasLimit) + enc.GasUsed = hexutil.Uint64(h.GasUsed) + enc.Time = hexutil.Uint64(h.Time) + enc.Extra = h.Extra + enc.MixDigest = h.MixDigest + enc.Nonce = h.Nonce + enc.Hash = h.Hash() + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (h *Header) UnmarshalJSON(input []byte) error { + type Header struct { + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` + Coinbase *common.Address `json:"miner" gencodec:"required"` + Root *common.Hash `json:"stateRoot" gencodec:"required"` + TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` + ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` + Number *hexutil.Big `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + Time *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` + MixDigest *common.Hash `json:"mixHash"` + Nonce *BlockNonce `json:"nonce"` + } + var dec Header + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.ParentHash == nil { + return errors.New("missing required field 'parentHash' for Header") + } + h.ParentHash = *dec.ParentHash + if dec.UncleHash == nil { + return errors.New("missing required field 'sha3Uncles' for Header") + } + h.UncleHash = *dec.UncleHash + if dec.Coinbase == nil { + return errors.New("missing required field 'miner' for Header") + } + h.Coinbase = *dec.Coinbase + if dec.Root == nil { + return errors.New("missing required field 'stateRoot' for Header") + } + h.Root = *dec.Root + if dec.TxHash == nil { + return errors.New("missing required field 'transactionsRoot' for Header") + } + h.TxHash = *dec.TxHash + if dec.ReceiptHash == nil { + return errors.New("missing required field 'receiptsRoot' for Header") + } + h.ReceiptHash = *dec.ReceiptHash + if dec.Bloom == nil { + return errors.New("missing required field 'logsBloom' for Header") + } + h.Bloom = *dec.Bloom + if dec.Difficulty == nil { + return errors.New("missing required field 'difficulty' for Header") + } + h.Difficulty = (*big.Int)(dec.Difficulty) + if dec.Number == nil { + return errors.New("missing required field 'number' for Header") + } + h.Number = (*big.Int)(dec.Number) + if dec.GasLimit == nil { + return errors.New("missing required field 'gasLimit' for Header") + } + h.GasLimit = uint64(*dec.GasLimit) + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for Header") + } + h.GasUsed = uint64(*dec.GasUsed) + if dec.Time == nil { + return errors.New("missing required field 'timestamp' for Header") + } + h.Time = uint64(*dec.Time) + if dec.Extra == nil { + return errors.New("missing required field 'extraData' for Header") + } + h.Extra = *dec.Extra + if dec.MixDigest != nil { + h.MixDigest = *dec.MixDigest + } + if dec.Nonce != nil { + h.Nonce = *dec.Nonce + } + return nil +} diff --git a/core/types/gen_log_json.go b/core/types/gen_log_json.go new file mode 100644 index 0000000..0e48863 --- /dev/null +++ b/core/types/gen_log_json.go @@ -0,0 +1,92 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" +) + +var _ = (*logMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (l Log) MarshalJSON() ([]byte, error) { + type Log struct { + Address common.Address `json:"address" gencodec:"required"` + Topics []common.Hash `json:"topics" gencodec:"required"` + Data hexutil.Bytes `json:"data" gencodec:"required"` + BlockNumber hexutil.Uint64 `json:"blockNumber"` + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + TxIndex hexutil.Uint `json:"transactionIndex" gencodec:"required"` + BlockHash common.Hash `json:"blockHash"` + Index hexutil.Uint `json:"logIndex" gencodec:"required"` + Removed bool `json:"removed"` + } + var enc Log + enc.Address = l.Address + enc.Topics = l.Topics + enc.Data = l.Data + enc.BlockNumber = hexutil.Uint64(l.BlockNumber) + enc.TxHash = l.TxHash + enc.TxIndex = hexutil.Uint(l.TxIndex) + enc.BlockHash = l.BlockHash + enc.Index = hexutil.Uint(l.Index) + enc.Removed = l.Removed + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (l *Log) UnmarshalJSON(input []byte) error { + type Log struct { + Address *common.Address `json:"address" gencodec:"required"` + Topics []common.Hash `json:"topics" gencodec:"required"` + Data *hexutil.Bytes `json:"data" gencodec:"required"` + BlockNumber *hexutil.Uint64 `json:"blockNumber"` + TxHash *common.Hash `json:"transactionHash" gencodec:"required"` + TxIndex *hexutil.Uint `json:"transactionIndex" gencodec:"required"` + BlockHash *common.Hash `json:"blockHash"` + Index *hexutil.Uint `json:"logIndex" gencodec:"required"` + Removed *bool `json:"removed"` + } + var dec Log + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.Address == nil { + return errors.New("missing required field 'address' for Log") + } + l.Address = *dec.Address + if dec.Topics == nil { + return errors.New("missing required field 'topics' for Log") + } + l.Topics = dec.Topics + if dec.Data == nil { + return errors.New("missing required field 'data' for Log") + } + l.Data = *dec.Data + if dec.BlockNumber != nil { + l.BlockNumber = uint64(*dec.BlockNumber) + } + if dec.TxHash == nil { + return errors.New("missing required field 'transactionHash' for Log") + } + l.TxHash = *dec.TxHash + if dec.TxIndex == nil { + return errors.New("missing required field 'transactionIndex' for Log") + } + l.TxIndex = uint(*dec.TxIndex) + if dec.BlockHash != nil { + l.BlockHash = *dec.BlockHash + } + if dec.Index == nil { + return errors.New("missing required field 'logIndex' for Log") + } + l.Index = uint(*dec.Index) + if dec.Removed != nil { + l.Removed = *dec.Removed + } + return nil +} diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go new file mode 100644 index 0000000..f7cde61 --- /dev/null +++ b/core/types/gen_receipt_json.go @@ -0,0 +1,104 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" +) + +var _ = (*receiptMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (r Receipt) MarshalJSON() ([]byte, error) { + type Receipt struct { + PostState hexutil.Bytes `json:"root"` + Status hexutil.Uint64 `json:"status"` + CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Logs []*Log `json:"logs" gencodec:"required"` + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + ContractAddress common.Address `json:"contractAddress"` + GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + BlockHash common.Hash `json:"blockHash,omitempty"` + BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` + TransactionIndex hexutil.Uint `json:"transactionIndex"` + } + var enc Receipt + enc.PostState = r.PostState + enc.Status = hexutil.Uint64(r.Status) + enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed) + enc.Bloom = r.Bloom + enc.Logs = r.Logs + enc.TxHash = r.TxHash + enc.ContractAddress = r.ContractAddress + enc.GasUsed = hexutil.Uint64(r.GasUsed) + enc.BlockHash = r.BlockHash + enc.BlockNumber = (*hexutil.Big)(r.BlockNumber) + enc.TransactionIndex = hexutil.Uint(r.TransactionIndex) + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (r *Receipt) UnmarshalJSON(input []byte) error { + type Receipt struct { + PostState *hexutil.Bytes `json:"root"` + Status *hexutil.Uint64 `json:"status"` + CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` + Bloom *Bloom `json:"logsBloom" gencodec:"required"` + Logs []*Log `json:"logs" gencodec:"required"` + TxHash *common.Hash `json:"transactionHash" gencodec:"required"` + ContractAddress *common.Address `json:"contractAddress"` + GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` + BlockHash *common.Hash `json:"blockHash,omitempty"` + BlockNumber *hexutil.Big `json:"blockNumber,omitempty"` + TransactionIndex *hexutil.Uint `json:"transactionIndex"` + } + var dec Receipt + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.PostState != nil { + r.PostState = *dec.PostState + } + if dec.Status != nil { + r.Status = uint64(*dec.Status) + } + if dec.CumulativeGasUsed == nil { + return errors.New("missing required field 'cumulativeGasUsed' for Receipt") + } + r.CumulativeGasUsed = uint64(*dec.CumulativeGasUsed) + if dec.Bloom == nil { + return errors.New("missing required field 'logsBloom' for Receipt") + } + r.Bloom = *dec.Bloom + if dec.Logs == nil { + return errors.New("missing required field 'logs' for Receipt") + } + r.Logs = dec.Logs + if dec.TxHash == nil { + return errors.New("missing required field 'transactionHash' for Receipt") + } + r.TxHash = *dec.TxHash + if dec.ContractAddress != nil { + r.ContractAddress = *dec.ContractAddress + } + if dec.GasUsed == nil { + return errors.New("missing required field 'gasUsed' for Receipt") + } + r.GasUsed = uint64(*dec.GasUsed) + if dec.BlockHash != nil { + r.BlockHash = *dec.BlockHash + } + if dec.BlockNumber != nil { + r.BlockNumber = (*big.Int)(dec.BlockNumber) + } + if dec.TransactionIndex != nil { + r.TransactionIndex = uint(*dec.TransactionIndex) + } + return nil +} diff --git a/core/types/gen_tx_json.go b/core/types/gen_tx_json.go new file mode 100644 index 0000000..0410632 --- /dev/null +++ b/core/types/gen_tx_json.go @@ -0,0 +1,101 @@ +// Code generated by github.com/fjl/gencodec. DO NOT EDIT. + +package types + +import ( + "encoding/json" + "errors" + "math/big" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" +) + +var _ = (*txdataMarshaling)(nil) + +// MarshalJSON marshals as JSON. +func (t txdata) MarshalJSON() ([]byte, error) { + type txdata struct { + AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"` + Price *hexutil.Big `json:"gasPrice" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` + Amount *hexutil.Big `json:"value" gencodec:"required"` + Payload hexutil.Bytes `json:"input" gencodec:"required"` + V *hexutil.Big `json:"v" gencodec:"required"` + R *hexutil.Big `json:"r" gencodec:"required"` + S *hexutil.Big `json:"s" gencodec:"required"` + Hash *common.Hash `json:"hash" rlp:"-"` + } + var enc txdata + enc.AccountNonce = hexutil.Uint64(t.AccountNonce) + enc.Price = (*hexutil.Big)(t.Price) + enc.GasLimit = hexutil.Uint64(t.GasLimit) + enc.Recipient = t.Recipient + enc.Amount = (*hexutil.Big)(t.Amount) + enc.Payload = t.Payload + enc.V = (*hexutil.Big)(t.V) + enc.R = (*hexutil.Big)(t.R) + enc.S = (*hexutil.Big)(t.S) + enc.Hash = t.Hash + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (t *txdata) UnmarshalJSON(input []byte) error { + type txdata struct { + AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"` + Price *hexutil.Big `json:"gasPrice" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` + Amount *hexutil.Big `json:"value" gencodec:"required"` + Payload *hexutil.Bytes `json:"input" gencodec:"required"` + V *hexutil.Big `json:"v" gencodec:"required"` + R *hexutil.Big `json:"r" gencodec:"required"` + S *hexutil.Big `json:"s" gencodec:"required"` + Hash *common.Hash `json:"hash" rlp:"-"` + } + var dec txdata + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.AccountNonce == nil { + return errors.New("missing required field 'nonce' for txdata") + } + t.AccountNonce = uint64(*dec.AccountNonce) + if dec.Price == nil { + return errors.New("missing required field 'gasPrice' for txdata") + } + t.Price = (*big.Int)(dec.Price) + if dec.GasLimit == nil { + return errors.New("missing required field 'gas' for txdata") + } + t.GasLimit = uint64(*dec.GasLimit) + if dec.Recipient != nil { + t.Recipient = dec.Recipient + } + if dec.Amount == nil { + return errors.New("missing required field 'value' for txdata") + } + t.Amount = (*big.Int)(dec.Amount) + if dec.Payload == nil { + return errors.New("missing required field 'input' for txdata") + } + t.Payload = *dec.Payload + if dec.V == nil { + return errors.New("missing required field 'v' for txdata") + } + t.V = (*big.Int)(dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' for txdata") + } + t.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' for txdata") + } + t.S = (*big.Int)(dec.S) + if dec.Hash != nil { + t.Hash = dec.Hash + } + return nil +} diff --git a/core/types/log.go b/core/types/log.go new file mode 100644 index 0000000..0b6184f --- /dev/null +++ b/core/types/log.go @@ -0,0 +1,143 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "io" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" + "github.com/ava-labs/go-ethereum/rlp" +) + +//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go + +// Log represents a contract log event. These events are generated by the LOG opcode and +// stored/indexed by the node. +type Log struct { + // Consensus fields: + // address of the contract that generated the event + Address common.Address `json:"address" gencodec:"required"` + // list of topics provided by the contract. + Topics []common.Hash `json:"topics" gencodec:"required"` + // supplied by the contract, usually ABI-encoded + Data []byte `json:"data" gencodec:"required"` + + // Derived fields. These fields are filled in by the node + // but not secured by consensus. + // block in which the transaction was included + BlockNumber uint64 `json:"blockNumber"` + // hash of the transaction + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + // index of the transaction in the block + TxIndex uint `json:"transactionIndex" gencodec:"required"` + // hash of the block in which the transaction was included + BlockHash common.Hash `json:"blockHash"` + // index of the log in the block + Index uint `json:"logIndex" gencodec:"required"` + + // The Removed field is true if this log was reverted due to a chain reorganisation. + // You must pay attention to this field if you receive logs through a filter query. + Removed bool `json:"removed"` +} + +type logMarshaling struct { + Data hexutil.Bytes + BlockNumber hexutil.Uint64 + TxIndex hexutil.Uint + Index hexutil.Uint +} + +type rlpLog struct { + Address common.Address + Topics []common.Hash + Data []byte +} + +// rlpStorageLog is the storage encoding of a log. +type rlpStorageLog rlpLog + +// legacyRlpStorageLog is the previous storage encoding of a log including some redundant fields. +type legacyRlpStorageLog struct { + Address common.Address + Topics []common.Hash + Data []byte + BlockNumber uint64 + TxHash common.Hash + TxIndex uint + BlockHash common.Hash + Index uint +} + +// EncodeRLP implements rlp.Encoder. +func (l *Log) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}) +} + +// DecodeRLP implements rlp.Decoder. +func (l *Log) DecodeRLP(s *rlp.Stream) error { + var dec rlpLog + err := s.Decode(&dec) + if err == nil { + l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data + } + return err +} + +// LogForStorage is a wrapper around a Log that flattens and parses the entire content of +// a log including non-consensus fields. +type LogForStorage Log + +// EncodeRLP implements rlp.Encoder. +func (l *LogForStorage) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, rlpStorageLog{ + Address: l.Address, + Topics: l.Topics, + Data: l.Data, + }) +} + +// DecodeRLP implements rlp.Decoder. +// +// Note some redundant fields(e.g. block number, tx hash etc) will be assembled later. +func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error { + blob, err := s.Raw() + if err != nil { + return err + } + var dec rlpStorageLog + err = rlp.DecodeBytes(blob, &dec) + if err == nil { + *l = LogForStorage{ + Address: dec.Address, + Topics: dec.Topics, + Data: dec.Data, + } + } else { + // Try to decode log with previous definition. + var dec legacyRlpStorageLog + err = rlp.DecodeBytes(blob, &dec) + if err == nil { + *l = LogForStorage{ + Address: dec.Address, + Topics: dec.Topics, + Data: dec.Data, + } + } + } + return err +} diff --git a/core/types/receipt.go b/core/types/receipt.go new file mode 100644 index 0000000..46538e8 --- /dev/null +++ b/core/types/receipt.go @@ -0,0 +1,336 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "bytes" + "errors" + "fmt" + "io" + "math/big" + "unsafe" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" + "github.com/ava-labs/go-ethereum/crypto" + "github.com/ava-labs/go-ethereum/rlp" +) + +//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go + +var ( + receiptStatusFailedRLP = []byte{} + receiptStatusSuccessfulRLP = []byte{0x01} +) + +const ( + // ReceiptStatusFailed is the status code of a transaction if execution failed. + ReceiptStatusFailed = uint64(0) + + // ReceiptStatusSuccessful is the status code of a transaction if execution succeeded. + ReceiptStatusSuccessful = uint64(1) +) + +// Receipt represents the results of a transaction. +type Receipt struct { + // Consensus fields: These fields are defined by the Yellow Paper + PostState []byte `json:"root"` + Status uint64 `json:"status"` + CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Logs []*Log `json:"logs" gencodec:"required"` + + // Implementation fields: These fields are added by geth when processing a transaction. + // They are stored in the chain database. + TxHash common.Hash `json:"transactionHash" gencodec:"required"` + ContractAddress common.Address `json:"contractAddress"` + GasUsed uint64 `json:"gasUsed" gencodec:"required"` + + // Inclusion information: These fields provide information about the inclusion of the + // transaction corresponding to this receipt. + BlockHash common.Hash `json:"blockHash,omitempty"` + BlockNumber *big.Int `json:"blockNumber,omitempty"` + TransactionIndex uint `json:"transactionIndex"` +} + +type receiptMarshaling struct { + PostState hexutil.Bytes + Status hexutil.Uint64 + CumulativeGasUsed hexutil.Uint64 + GasUsed hexutil.Uint64 + BlockNumber *hexutil.Big + TransactionIndex hexutil.Uint +} + +// receiptRLP is the consensus encoding of a receipt. +type receiptRLP struct { + PostStateOrStatus []byte + CumulativeGasUsed uint64 + Bloom Bloom + Logs []*Log +} + +// storedReceiptRLP is the storage encoding of a receipt. +type storedReceiptRLP struct { + PostStateOrStatus []byte + CumulativeGasUsed uint64 + Logs []*LogForStorage +} + +// v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4. +type v4StoredReceiptRLP struct { + PostStateOrStatus []byte + CumulativeGasUsed uint64 + TxHash common.Hash + ContractAddress common.Address + Logs []*LogForStorage + GasUsed uint64 +} + +// v3StoredReceiptRLP is the original storage encoding of a receipt including some unnecessary fields. +type v3StoredReceiptRLP struct { + PostStateOrStatus []byte + CumulativeGasUsed uint64 + Bloom Bloom + TxHash common.Hash + ContractAddress common.Address + Logs []*LogForStorage + GasUsed uint64 +} + +// NewReceipt creates a barebone transaction receipt, copying the init fields. +func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { + r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed} + if failed { + r.Status = ReceiptStatusFailed + } else { + r.Status = ReceiptStatusSuccessful + } + return r +} + +// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt +// into an RLP stream. If no post state is present, byzantium fork is assumed. +func (r *Receipt) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}) +} + +// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt +// from an RLP stream. +func (r *Receipt) DecodeRLP(s *rlp.Stream) error { + var dec receiptRLP + if err := s.Decode(&dec); err != nil { + return err + } + if err := r.setStatus(dec.PostStateOrStatus); err != nil { + return err + } + r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs + return nil +} + +func (r *Receipt) setStatus(postStateOrStatus []byte) error { + switch { + case bytes.Equal(postStateOrStatus, receiptStatusSuccessfulRLP): + r.Status = ReceiptStatusSuccessful + case bytes.Equal(postStateOrStatus, receiptStatusFailedRLP): + r.Status = ReceiptStatusFailed + case len(postStateOrStatus) == len(common.Hash{}): + r.PostState = postStateOrStatus + default: + return fmt.Errorf("invalid receipt status %x", postStateOrStatus) + } + return nil +} + +func (r *Receipt) statusEncoding() []byte { + if len(r.PostState) == 0 { + if r.Status == ReceiptStatusFailed { + return receiptStatusFailedRLP + } + return receiptStatusSuccessfulRLP + } + return r.PostState +} + +// Size returns the approximate memory used by all internal contents. It is used +// to approximate and limit the memory consumption of various caches. +func (r *Receipt) Size() common.StorageSize { + size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState)) + + size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{})) + for _, log := range r.Logs { + size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data)) + } + return size +} + +// ReceiptForStorage is a wrapper around a Receipt that flattens and parses the +// entire content of a receipt, as opposed to only the consensus fields originally. +type ReceiptForStorage Receipt + +// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt +// into an RLP stream. +func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { + enc := &storedReceiptRLP{ + PostStateOrStatus: (*Receipt)(r).statusEncoding(), + CumulativeGasUsed: r.CumulativeGasUsed, + Logs: make([]*LogForStorage, len(r.Logs)), + } + for i, log := range r.Logs { + enc.Logs[i] = (*LogForStorage)(log) + } + return rlp.Encode(w, enc) +} + +// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation +// fields of a receipt from an RLP stream. +func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { + // Retrieve the entire receipt blob as we need to try multiple decoders + blob, err := s.Raw() + if err != nil { + return err + } + // Try decoding from the newest format for future proofness, then the older one + // for old nodes that just upgraded. V4 was an intermediate unreleased format so + // we do need to decode it, but it's not common (try last). + if err := decodeStoredReceiptRLP(r, blob); err == nil { + return nil + } + if err := decodeV3StoredReceiptRLP(r, blob); err == nil { + return nil + } + return decodeV4StoredReceiptRLP(r, blob) +} + +func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { + var stored storedReceiptRLP + if err := rlp.DecodeBytes(blob, &stored); err != nil { + return err + } + if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil { + return err + } + r.CumulativeGasUsed = stored.CumulativeGasUsed + r.Logs = make([]*Log, len(stored.Logs)) + for i, log := range stored.Logs { + r.Logs[i] = (*Log)(log) + } + r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) + + return nil +} + +func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { + var stored v4StoredReceiptRLP + if err := rlp.DecodeBytes(blob, &stored); err != nil { + return err + } + if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil { + return err + } + r.CumulativeGasUsed = stored.CumulativeGasUsed + r.TxHash = stored.TxHash + r.ContractAddress = stored.ContractAddress + r.GasUsed = stored.GasUsed + r.Logs = make([]*Log, len(stored.Logs)) + for i, log := range stored.Logs { + r.Logs[i] = (*Log)(log) + } + r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) + + return nil +} + +func decodeV3StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { + var stored v3StoredReceiptRLP + if err := rlp.DecodeBytes(blob, &stored); err != nil { + return err + } + if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil { + return err + } + r.CumulativeGasUsed = stored.CumulativeGasUsed + r.Bloom = stored.Bloom + r.TxHash = stored.TxHash + r.ContractAddress = stored.ContractAddress + r.GasUsed = stored.GasUsed + r.Logs = make([]*Log, len(stored.Logs)) + for i, log := range stored.Logs { + r.Logs[i] = (*Log)(log) + } + return nil +} + +// Receipts is a wrapper around a Receipt array to implement DerivableList. +type Receipts []*Receipt + +// Len returns the number of receipts in this list. +func (r Receipts) Len() int { return len(r) } + +// GetRlp returns the RLP encoding of one receipt from the list. +func (r Receipts) GetRlp(i int) []byte { + bytes, err := rlp.EncodeToBytes(r[i]) + if err != nil { + panic(err) + } + return bytes +} + +// DeriveFields fills the receipts with their computed fields based on consensus +// data and contextual infos like containing block and transactions. +func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error { + signer := MakeSigner(config, new(big.Int).SetUint64(number)) + + logIndex := uint(0) + if len(txs) != len(r) { + return errors.New("transaction and receipt count mismatch") + } + for i := 0; i < len(r); i++ { + // The transaction hash can be retrieved from the transaction itself + r[i].TxHash = txs[i].Hash() + + // block location fields + r[i].BlockHash = hash + r[i].BlockNumber = new(big.Int).SetUint64(number) + r[i].TransactionIndex = uint(i) + + // The contract address can be derived from the transaction itself + if txs[i].To() == nil { + // Deriving the signer is expensive, only do if it's actually needed + from, _ := Sender(signer, txs[i]) + r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce()) + } + // The used gas can be calculated based on previous r + if i == 0 { + r[i].GasUsed = r[i].CumulativeGasUsed + } else { + r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed + } + // The derived log fields can simply be set from the block and transaction + for j := 0; j < len(r[i].Logs); j++ { + r[i].Logs[j].BlockNumber = number + r[i].Logs[j].BlockHash = hash + r[i].Logs[j].TxHash = r[i].TxHash + r[i].Logs[j].TxIndex = uint(i) + r[i].Logs[j].Index = logIndex + logIndex++ + } + } + return nil +} diff --git a/core/types/transaction.go b/core/types/transaction.go new file mode 100644 index 0000000..cf9e61a --- /dev/null +++ b/core/types/transaction.go @@ -0,0 +1,440 @@ +// Copyright 2014 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package types + +import ( + "container/heap" + "errors" + "io" + "math/big" + "sync/atomic" + + "github.com/ava-labs/go-ethereum/common" + "github.com/ava-labs/go-ethereum/common/hexutil" + "github.com/ava-labs/go-ethereum/crypto" + "github.com/ava-labs/go-ethereum/rlp" +) + +//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go + +var ( + ErrInvalidSig = errors.New("invalid transaction v, r, s values") +) + +type Transaction struct { + data txdata + // caches + hash atomic.Value + size atomic.Value + from atomic.Value +} + +type txdata struct { + AccountNonce uint64 `json:"nonce" gencodec:"required"` + Price *big.Int `json:"gasPrice" gencodec:"required"` + GasLimit uint64 `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation + Amount *big.Int `json:"value" gencodec:"required"` + CoinID *common.Hash `json:"coinid" rlp:"-"` + Amount2 *big.Int `json:"value2"` + Payload []byte `json:"input" gencodec:"required"` + + // Signature values + V *big.Int `json:"v" gencodec:"required"` + R *big.Int `json:"r" gencodec:"required"` + S *big.Int `json:"s" gencodec:"required"` + + // This is only used when marshaling to JSON. + Hash *common.Hash `json:"hash" rlp:"-"` +} + +type txdataMarshaling struct { + AccountNonce hexutil.Uint64 + Price *hexutil.Big + GasLimit hexutil.Uint64 + Amount *hexutil.Big + CoinID *hexutil.Bytes + Amount2 *hexutil.Big + Payload hexutil.Bytes + V *hexutil.Big + R *hexutil.Big + S *hexutil.Big +} + +func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) +} + +func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) +} + +func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + if len(data) > 0 { + data = common.CopyBytes(data) + } + d := txdata{ + AccountNonce: nonce, + Recipient: to, + Payload: data, + Amount: new(big.Int), + CoinID: nil, + Amount2: new(big.Int), + GasLimit: gasLimit, + Price: new(big.Int), + V: new(big.Int), + R: new(big.Int), + S: new(big.Int), + } + if amount != nil { + d.Amount.Set(amount) + } + if gasPrice != nil { + d.Price.Set(gasPrice) + } + + return &Transaction{data: d} +} + +func (tx *Transaction) SetMultiCoinValue(coinID *common.Hash, amount *big.Int) { + tx.data.CoinID = coinID + tx.data.Amount2.Set(amount) +} + +// ChainId returns which chain id this transaction was signed for (if at all) +func (tx *Transaction) ChainId() *big.Int { + return deriveChainId(tx.data.V) +} + +// Protected returns whether the transaction is protected from replay protection. +func (tx *Transaction) Protected() bool { + return isProtectedV(tx.data.V) +} + +func isProtectedV(V *big.Int) bool { + if V.BitLen() <= 8 { + v := V.Uint64() + return v != 27 && v != 28 + } + // anything not 27 or 28 is considered protected + return true +} + +// EncodeRLP implements rlp.Encoder +func (tx *Transaction) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, &tx.data) +} + +// DecodeRLP implements rlp.Decoder +func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { + _, size, _ := s.Kind() + err := s.Decode(&tx.data) + if err == nil { + tx.size.Store(common.StorageSize(rlp.ListSize(size))) + } + + return err +} + +// MarshalJSON encodes the web3 RPC transaction format. +func (tx *Transaction) MarshalJSON() ([]byte, error) { + hash := tx.Hash() + data := tx.data + data.Hash = &hash + return data.MarshalJSON() +} + +// UnmarshalJSON decodes the web3 RPC transaction format. +func (tx *Transaction) UnmarshalJSON(input []byte) error { + var dec txdata + if err := dec.UnmarshalJSON(input); err != nil { + return err + } + + withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0 + if withSignature { + var V byte + if isProtectedV(dec.V) { + chainID := deriveChainId(dec.V).Uint64() + V = byte(dec.V.Uint64() - 35 - 2*chainID) + } else { + V = byte(dec.V.Uint64() - 27) + } + if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) { + return ErrInvalidSig + } + } + + *tx = Transaction{data: dec} + return nil +} + +func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } +func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } +func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } +func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } +func (tx *Transaction) CoinID() *big.Int { return big.NewInt(0) } +func (tx *Transaction) Value2() *big.Int { return big.NewInt(0) } +func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } +func (tx *Transaction) CheckNonce() bool { return true } + +// To returns the recipient address of the transaction. +// It returns nil if the transaction is a contract creation. +func (tx *Transaction) To() *common.Address { + if tx.data.Recipient == nil { + return nil + } + to := *tx.data.Recipient + return &to +} + +// Hash hashes the RLP encoding of tx. +// It uniquely identifies the transaction. +func (tx *Transaction) Hash() common.Hash { + if hash := tx.hash.Load(); hash != nil { + return hash.(common.Hash) + } + v := rlpHash(tx) + tx.hash.Store(v) + return v +} + +// Size returns the true RLP encoded storage size of the transaction, either by +// encoding and returning it, or returning a previsouly cached value. +func (tx *Transaction) Size() common.StorageSize { + if size := tx.size.Load(); size != nil { + return size.(common.StorageSize) + } + c := writeCounter(0) + rlp.Encode(&c, &tx.data) + tx.size.Store(common.StorageSize(c)) + return common.StorageSize(c) +} + +// AsMessage returns the transaction as a core.Message. +// +// AsMessage requires a signer to derive the sender. +// +// XXX Rename message to something less arbitrary? +func (tx *Transaction) AsMessage(s Signer) (Message, error) { + msg := Message{ + nonce: tx.data.AccountNonce, + gasLimit: tx.data.GasLimit, + gasPrice: new(big.Int).Set(tx.data.Price), + to: tx.data.Recipient, + amount: tx.data.Amount, + coinID: tx.data.CoinID, + amount2: tx.data.Amount2, + data: tx.data.Payload, + checkNonce: true, + } + + var err error + msg.from, err = Sender(s, tx) + return msg, err +} + +// WithSignature returns a new transaction with the given signature. +// This signature needs to be in the [R || S || V] format where V is 0 or 1. +func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) { + r, s, v, err := signer.SignatureValues(tx, sig) + if err != nil { + return nil, err + } + cpy := &Transaction{data: tx.data} + cpy.data.R, cpy.data.S, cpy.data.V = r, s, v + return cpy, nil +} + +// Cost returns amount + gasprice * gaslimit. +func (tx *Transaction) Cost() *big.Int { + total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit)) + total.Add(total, tx.data.Amount) + return total +} + +// RawSignatureValues returns the V, R, S signature values of the transaction. +// The return values should not be modified by the caller. +func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) { + return tx.data.V, tx.data.R, tx.data.S +} + +// Transactions is a Transaction slice type for basic sorting. +type Transactions []*Transaction + +// Len returns the length of s. +func (s Transactions) Len() int { return len(s) } + +// Swap swaps the i'th and the j'th element in s. +func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// GetRlp implements Rlpable and returns the i'th element of s in rlp. +func (s Transactions) GetRlp(i int) []byte { + enc, _ := rlp.EncodeToBytes(s[i]) + return enc +} + +// TxDifference returns a new set which is the difference between a and b. +func TxDifference(a, b Transactions) Transactions { + keep := make(Transactions, 0, len(a)) + + remove := make(map[common.Hash]struct{}) + for _, tx := range b { + remove[tx.Hash()] = struct{}{} + } + + for _, tx := range a { + if _, ok := remove[tx.Hash()]; !ok { + keep = append(keep, tx) + } + } + + return keep +} + +// TxByNonce implements the sort interface to allow sorting a list of transactions +// by their nonces. This is usually only useful for sorting transactions from a +// single account, otherwise a nonce comparison doesn't make much sense. +type TxByNonce Transactions + +func (s TxByNonce) Len() int { return len(s) } +func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce } +func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// TxByPrice implements both the sort and the heap interface, making it useful +// for all at once sorting as well as individually adding and removing elements. +type TxByPrice Transactions + +func (s TxByPrice) Len() int { return len(s) } +func (s TxByPrice) Less(i, j int) bool { return s[i].data.Price.Cmp(s[j].data.Price) > 0 } +func (s TxByPrice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func (s *TxByPrice) Push(x interface{}) { + *s = append(*s, x.(*Transaction)) +} + +func (s *TxByPrice) Pop() interface{} { + old := *s + n := len(old) + x := old[n-1] + *s = old[0 : n-1] + return x +} + +// TransactionsByPriceAndNonce represents a set of transactions that can return +// transactions in a profit-maximizing sorted order, while supporting removing +// entire batches of transactions for non-executable accounts. +type TransactionsByPriceAndNonce struct { + txs map[common.Address]Transactions // Per account nonce-sorted list of transactions + heads TxByPrice // Next transaction for each unique account (price heap) + signer Signer // Signer for the set of transactions +} + +// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve +// price sorted transactions in a nonce-honouring way. +// +// Note, the input map is reowned so the caller should not interact any more with +// if after providing it to the constructor. +func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { + // Initialize a price based heap with the head transactions + heads := make(TxByPrice, 0, len(txs)) + for from, accTxs := range txs { + heads = append(heads, accTxs[0]) + // Ensure the sender address is from the signer + acc, _ := Sender(signer, accTxs[0]) + txs[acc] = accTxs[1:] + if from != acc { + delete(txs, from) + } + } + heap.Init(&heads) + + // Assemble and return the transaction set + return &TransactionsByPriceAndNonce{ + txs: txs, + heads: heads, + signer: signer, + } +} + +// Peek returns the next transaction by price. +func (t *TransactionsByPriceAndNonce) Peek() *Transaction { + if len(t.heads) == 0 { + return nil + } + return t.heads[0] +} + +// Shift replaces the current best head with the next one from the same account. +func (t *TransactionsByPriceAndNonce) Shift() { + acc, _ := Sender(t.signer, t.heads[0]) + if txs, ok := t.txs[acc]; ok && len(txs) > 0 { + t.heads[0], t.txs[acc] = txs[0], txs[1:] + heap.Fix(&t.heads, 0) + } else { + heap.Pop(&t.heads) + } +} + +// Pop removes the best transaction, *not* replacing it with the next one from +// the same account. This should be used when a transaction cannot be ex