diff options
author | JM <jm@dexon.org> | 2019-01-31 15:12:57 +0800 |
---|---|---|
committer | Jhih-Ming Huang <jm.huang@cobinhood.com> | 2019-03-26 17:48:21 +0800 |
commit | 1ea57eed7e9e37b40d27b11fe316cfe3569fade6 (patch) | |
tree | 9f5b919f6c75e6dd713902ecc96a0c845679649a /core/vm/tools | |
parent | 1810c3af03d3da56d42f93c4521e33332156dcb6 (diff) | |
download | dexon-1ea57eed7e9e37b40d27b11fe316cfe3569fade6.tar dexon-1ea57eed7e9e37b40d27b11fe316cfe3569fade6.tar.gz dexon-1ea57eed7e9e37b40d27b11fe316cfe3569fade6.tar.bz2 dexon-1ea57eed7e9e37b40d27b11fe316cfe3569fade6.tar.lz dexon-1ea57eed7e9e37b40d27b11fe316cfe3569fade6.tar.xz dexon-1ea57eed7e9e37b40d27b11fe316cfe3569fade6.tar.zst dexon-1ea57eed7e9e37b40d27b11fe316cfe3569fade6.zip |
core: vm: vm interface (#164)
Diffstat (limited to 'core/vm/tools')
-rw-r--r-- | core/vm/tools/patch.go | 158 | ||||
-rw-r--r-- | core/vm/tools/patch_example_test.go | 26 | ||||
-rw-r--r-- | core/vm/tools/transaction.go | 263 |
3 files changed, 447 insertions, 0 deletions
diff --git a/core/vm/tools/patch.go b/core/vm/tools/patch.go new file mode 100644 index 000000000..7d41fcc98 --- /dev/null +++ b/core/vm/tools/patch.go @@ -0,0 +1,158 @@ +package tools + +import ( + "encoding/binary" + "encoding/hex" + "regexp" +) + +func PatchBinary(input []byte) []byte { + if input == nil { + return nil + } + stringCode := hex.EncodeToString(input) + stringCode = Patch(stringCode) + result, _ := hex.DecodeString(stringCode) + return result +} +func patchPattern1(input string) string { + // Case 1 + // PUSH1 v1 DUP1 PUSH1 v2 PUSH1 v3 CODECOPY + // we have to set v1 = v1 + 1 + r, _ := regexp.Compile("60..8060..60..39") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 1 + bString, _ := hex.DecodeString(input) + bString[valLoc]++ + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern2(input string) string { + // Case 2 + // PUSH2 v1 v2 DUP1 PUSH2 v3 PUSH1 v4 CODECOPY + // we have to set BigEndian(v1,v2)++ + r, _ := regexp.Compile("61....8061....60..39") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 1 + bString, _ := hex.DecodeString(input) + val := binary.BigEndian.Uint16(bString[valLoc : valLoc+2]) + val++ + binary.BigEndian.PutUint16(bString[valLoc:], val) + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern3(input string) string { + // Case 3 + // PUSH1 v1 DUP1 PUSH3 v2 v3 v4 DUP4 CODECOPY + // we have to set BigEndian(v2,v3,v4)++ + r, _ := regexp.Compile("60..8062......8339") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 4 + bString, _ := hex.DecodeString(input) + toConvert := append([]byte{0}, bString[valLoc:valLoc+3]...) + tmpBinary := make([]byte, 4) + val := binary.BigEndian.Uint32(toConvert) + val-- + binary.BigEndian.PutUint32(tmpBinary, val) + bString[valLoc] = tmpBinary[1] + bString[valLoc+1] = tmpBinary[2] + bString[valLoc+2] = tmpBinary[3] + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern4(input string) string { + // Case 4 + // PUSH2 v1 v2 DUP1 PUSH2 v3 DUP4 CODECOPY + // we have to set BigEndian(v1,v2)+2 + r, _ := regexp.Compile("61....8061....8339") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 1 + bString, _ := hex.DecodeString(input) + val := binary.BigEndian.Uint16(bString[valLoc : valLoc+2]) + val = val + 2 + binary.BigEndian.PutUint16(bString[valLoc:], val) + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern5(input string) string { + // Case 5 + // PUSH1 v1 DUP1 PUSH2 v2 v3 DUP4 CODECOPY + // we have to set BigEndian(v2,v3)++ + r, _ := regexp.Compile("60..8061....8339") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 4 + bString, _ := hex.DecodeString(input) + val := binary.BigEndian.Uint16(bString[valLoc : valLoc+2]) + val = val + 1 + binary.BigEndian.PutUint16(bString[valLoc:], val) + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern6(input string) string { + // Case 6 + // SUB DUP1 PUSH2 v2 v3 DUP4 CODECOPY + // we have to set BigEndian(v2,v3)++ + r, _ := regexp.Compile("038061....8339") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 3 + bString, _ := hex.DecodeString(input) + val := binary.BigEndian.Uint16(bString[valLoc : valLoc+2]) + val = val + 1 + binary.BigEndian.PutUint16(bString[valLoc:], val) + input = hex.EncodeToString(bString) + } + return input +} +func addPrefix(input string) string { + r, _ := regexp.Compile("6060604052") + loc := r.FindAllStringIndex(input, -1) + if len(loc) > 0 { + for i, offset := range loc { + insertLoc := offset[0] + i*2 + if !isDelegateCall(input, insertLoc) { + input = input[:insertLoc] + "00" + input[insertLoc:] + } + } + if input[0:2] != "00" { + input = "00" + input + } + } + return input +} +func isDelegateCall(input string, codeStartAt int) bool { + delegateCallPrefix := "6504032353da7150" + prefixLen := len(delegateCallPrefix) + newStartAt := codeStartAt - prefixLen + if newStartAt >= 0 { + if input[newStartAt:newStartAt+prefixLen] == delegateCallPrefix { + return true + } + } + return false +} + +// Patch patch lagacy bytecode(hex string) to new vm interface compatible bytecode +func Patch(input string) string { + if len(input) == 0 { + return input + } + input = patchPattern1(input) + input = patchPattern2(input) + input = patchPattern3(input) + input = patchPattern4(input) + input = patchPattern5(input) + input = patchPattern6(input) + result := addPrefix(input) + return result +} diff --git a/core/vm/tools/patch_example_test.go b/core/vm/tools/patch_example_test.go new file mode 100644 index 000000000..7c093e68f --- /dev/null +++ b/core/vm/tools/patch_example_test.go @@ -0,0 +1,26 @@ +package tools + +import ( + "fmt" +) + +func ExamplePatchPUSH1() { + input := "606060405260068060106000396000f3606060405200" + // 07 ^00 + fmt.Println(Patch(input)) + // Output: + // 00606060405260078060106000396000f300606060405200 +} +func ExamplePatchPUSH2() { + input := "6060604052aabb616fff8606060405200" + // 7000 ^00 + fmt.Println(Patch(input)) + // Output: + // 006060604052aabb616fff800606060405200 +} +func ExamplePatchDelegate() { + input := "6504032353da71506060604052" + fmt.Println(Patch(input)) + // Output: + // 006504032353da71506060604052 +} diff --git a/core/vm/tools/transaction.go b/core/vm/tools/transaction.go new file mode 100644 index 000000000..1ff36a36e --- /dev/null +++ b/core/vm/tools/transaction.go @@ -0,0 +1,263 @@ +package tools + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "sync/atomic" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/hexutil" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/crypto/sha3" + "github.com/dexon-foundation/dexon/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 writeCounter common.StorageSize + +func (c *writeCounter) Write(b []byte) (int, error) { + *c += writeCounter(len(b)) + return len(b), nil +} + +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"` + 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 + Payload hexutil.Bytes + V *hexutil.Big + R *hexutil.Big + S *hexutil.Big +} + +// 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 +} + +func (tx *Transaction) Size() common.StorageSize { + if size := tx.size.Load(); size != nil { + fmt.Println("tools load") + return size.(common.StorageSize) + } + c := writeCounter(0) + rlp.Encode(&c, &tx.Data) + tx.size.Store(common.StorageSize(c)) + return common.StorageSize(c) +} + +// 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() +} + +// 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 +} + +// 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 +} + +// deriveChainId derives the chain id from the given v parameter +func deriveChainId(v *big.Int) *big.Int { + if v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + return new(big.Int).SetUint64((v - 35) / 2) + } + v = new(big.Int).Sub(v, big.NewInt(35)) + return v.Div(v, big.NewInt(2)) +} + +// 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 +} +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewKeccak256() + rlp.Encode(hw, x) + hw.Sum(h[:0]) + return h +} +func (tx *Transaction) SetFrom(from atomic.Value) { + tx.from.Store(from.Load()) +} +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) Nonce() uint64 { return tx.Data.AccountNonce } +func (tx *Transaction) CheckNonce() bool { return true } |