-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
582 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package ethereum | ||
|
||
import ( | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/renproject/multichain/api/address" | ||
"github.com/renproject/pack" | ||
"github.com/renproject/surge" | ||
) | ||
|
||
// AddressEncodeDecoder implements the address.EncodeDecoder interface | ||
type AddressEncodeDecoder struct { | ||
AddressEncoder | ||
AddressDecoder | ||
} | ||
|
||
// AddressEncoder implements the address.Encoder interface. | ||
type AddressEncoder interface { | ||
EncodeAddress(address.RawAddress) (address.Address, error) | ||
} | ||
|
||
type addressEncoder struct{} | ||
|
||
// NewAddressEncodeDecoder constructs a new AddressEncodeDecoder. | ||
func NewAddressEncodeDecoder() address.EncodeDecoder { | ||
return AddressEncodeDecoder{ | ||
AddressEncoder: NewAddressEncoder(), | ||
AddressDecoder: NewAddressDecoder(), | ||
} | ||
} | ||
|
||
// AddressDecoder implements the address.Decoder interface. | ||
type AddressDecoder interface { | ||
DecodeAddress(address.Address) (address.RawAddress, error) | ||
} | ||
|
||
type addressDecoder struct{} | ||
|
||
// NewAddressDecoder constructs a new AddressDecoder. | ||
func NewAddressDecoder() AddressDecoder { | ||
return addressDecoder{} | ||
} | ||
|
||
// NewAddressEncoder constructs a new AddressEncoder. | ||
func NewAddressEncoder() AddressEncoder { | ||
return addressEncoder{} | ||
} | ||
|
||
func (addressDecoder) DecodeAddress(encoded address.Address) (address.RawAddress, error) { | ||
ethaddr, err := NewAddressFromHex(string(pack.String(encoded))) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return address.RawAddress(pack.Bytes(ethaddr[:])), nil | ||
} | ||
|
||
func (addressEncoder) EncodeAddress(rawAddr address.RawAddress) (address.Address, error) { | ||
encodedAddr := common.Bytes2Hex([]byte(rawAddr)) | ||
return address.Address(pack.NewString(encodedAddr)), nil | ||
} | ||
|
||
// An Address represents a public address on the Ethereum blockchain. It can be | ||
// the address of an external account, or the address of a smart contract. | ||
type Address common.Address | ||
|
||
// NewAddressFromHex returns an Address decoded from a hex | ||
// string. | ||
func NewAddressFromHex(str string) (Address, error) { | ||
if strings.HasPrefix(str, "0x") { | ||
str = str[2:] | ||
} | ||
if len(str) != 40 { | ||
return Address{}, fmt.Errorf("invalid ethaddress %v", str) | ||
} | ||
ethaddrData, err := hex.DecodeString(str) | ||
if err != nil { | ||
return Address{}, fmt.Errorf("invalid ethaddress %v: %v", str, err) | ||
} | ||
ethaddr := common.Address{} | ||
copy(ethaddr[:], ethaddrData) | ||
return Address(ethaddr), nil | ||
} | ||
|
||
// SizeHint returns the number of bytes needed to represent this address in | ||
// binary. | ||
func (Address) SizeHint() int { | ||
return common.AddressLength | ||
} | ||
|
||
// Marshal the address to binary. | ||
func (addr Address) Marshal(buf []byte, rem int) ([]byte, int, error) { | ||
if len(buf) < common.AddressLength || rem < common.AddressLength { | ||
return buf, rem, surge.ErrUnexpectedEndOfBuffer | ||
} | ||
copy(buf, addr[:]) | ||
return buf[common.AddressLength:], rem - common.AddressLength, nil | ||
} | ||
|
||
// Unmarshal the address from binary. | ||
func (addr *Address) Unmarshal(buf []byte, rem int) ([]byte, int, error) { | ||
if len(buf) < common.AddressLength || rem < common.AddressLength { | ||
return buf, rem, surge.ErrUnexpectedEndOfBuffer | ||
} | ||
copy(addr[:], buf[:common.AddressLength]) | ||
return buf[common.AddressLength:], rem - common.AddressLength, nil | ||
} | ||
|
||
// MarshalJSON implements JSON marshaling by encoding the address as a hex | ||
// string. | ||
func (addr Address) MarshalJSON() ([]byte, error) { | ||
return json.Marshal(common.Address(addr).Hex()) | ||
} | ||
|
||
// UnmarshalJSON implements JSON unmarshaling by expected the data be a hex | ||
// encoded string representation of an address. | ||
func (addr *Address) UnmarshalJSON(data []byte) error { | ||
var str string | ||
if err := json.Unmarshal(data, &str); err != nil { | ||
return err | ||
} | ||
ethaddr, err := NewAddressFromHex(str) | ||
if err != nil { | ||
return err | ||
} | ||
*addr = ethaddr | ||
return nil | ||
} | ||
|
||
// String returns the address as a human-readable hex string. | ||
func (addr Address) String() string { | ||
return hex.EncodeToString(addr[:]) | ||
} | ||
|
||
// Bytes returns the address as a slice of 20 bytes. | ||
func (addr Address) Bytes() pack.Bytes { | ||
return pack.Bytes(addr[:]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package ethereum_test | ||
|
||
import ( | ||
"encoding/hex" | ||
"encoding/json" | ||
"testing/quick" | ||
|
||
"github.com/renproject/multichain/chain/ethereum" | ||
"github.com/renproject/surge" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("Address", func() { | ||
Context("when unmarshaling and unmarshaling", func() { | ||
It("should equal itself", func() { | ||
f := func(x [20]byte) bool { | ||
addr := ethereum.Address(x) | ||
Expect(addr.SizeHint()).To(Equal(20)) | ||
|
||
bytes, err := surge.ToBinary(addr) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
var newAddr ethereum.Address | ||
err = surge.FromBinary(&newAddr, bytes) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
Expect(addr).To(Equal(newAddr)) | ||
return true | ||
} | ||
|
||
err := quick.Check(f, nil) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
}) | ||
|
||
Context("when unmarshaling and unmarshaling to/from JSON", func() { | ||
It("should equal itself", func() { | ||
f := func(x [20]byte) bool { | ||
addr := ethereum.Address(x) | ||
|
||
bytes, err := json.Marshal(addr) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
var newAddr ethereum.Address | ||
err = json.Unmarshal(bytes, &newAddr) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
Expect(addr).To(Equal(newAddr)) | ||
return true | ||
} | ||
|
||
err := quick.Check(f, nil) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
|
||
Context("when the address is invalid hex", func() { | ||
It("should return an error", func() { | ||
f := func(x [40]byte) bool { | ||
bytes, err := json.Marshal(string(x[:])) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
var newAddr ethereum.Address | ||
err = json.Unmarshal(bytes, &newAddr) | ||
Expect(err).To(HaveOccurred()) | ||
return true | ||
} | ||
|
||
err := quick.Check(f, nil) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
}) | ||
|
||
Context("when the address is invalid length", func() { | ||
It("should return an error", func() { | ||
f := func(x [10]byte) bool { | ||
addr := hex.EncodeToString(x[:]) | ||
bytes, err := json.Marshal(addr) | ||
Expect(err).ToNot(HaveOccurred()) | ||
|
||
var newAddr ethereum.Address | ||
err = json.Unmarshal(bytes, &newAddr) | ||
Expect(err).To(HaveOccurred()) | ||
return true | ||
} | ||
|
||
err := quick.Check(f, nil) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
}) | ||
}) | ||
|
||
Context("when unmarshalling random data", func() { | ||
It("should not panic", func() { | ||
f := func(x []byte) bool { | ||
var addr ethereum.Address | ||
Expect(func() { addr.Unmarshal(x, surge.MaxBytes) }).ToNot(Panic()) | ||
Expect(func() { json.Unmarshal(x, &addr) }).ToNot(Panic()) | ||
return true | ||
} | ||
|
||
err := quick.Check(f, nil) | ||
Expect(err).ToNot(HaveOccurred()) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package ethereum | ||
|
||
import ( | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/ethereum/go-ethereum/accounts/abi" | ||
"github.com/renproject/pack" | ||
) | ||
|
||
// A Payload is an Ethereum encoded function call. It includes an ABI, the | ||
// function being called from the ABI, and the data being passed to the | ||
// function. | ||
type Payload struct { | ||
ABI pack.Bytes `json:"abi"` | ||
Fn pack.Bytes `json:"fn"` | ||
Data pack.Bytes `json:"data"` | ||
} | ||
|
||
// Encode values into an Ethereum ABI compatible byte slice. | ||
func Encode(vals ...interface{}) []byte { | ||
ethargs := make(abi.Arguments, 0, len(vals)) | ||
ethvals := make([]interface{}, 0, len(vals)) | ||
|
||
for _, val := range vals { | ||
var ethval interface{} | ||
var ty abi.Type | ||
var err error | ||
|
||
switch val := val.(type) { | ||
case pack.Bytes: | ||
ethval = val | ||
ty, err = abi.NewType("bytes", "", nil) | ||
case pack.Bytes32: | ||
ethval = val | ||
ty, err = abi.NewType("bytes32", "", nil) | ||
|
||
case pack.U8: | ||
ethval = big.NewInt(0).SetUint64(uint64(val.Uint8())) | ||
ty, err = abi.NewType("uint256", "", nil) | ||
case pack.U16: | ||
ethval = big.NewInt(0).SetUint64(uint64(val.Uint16())) | ||
ty, err = abi.NewType("uint256", "", nil) | ||
case pack.U32: | ||
ethval = big.NewInt(0).SetUint64(uint64(val.Uint32())) | ||
ty, err = abi.NewType("uint256", "", nil) | ||
case pack.U64: | ||
ethval = big.NewInt(0).SetUint64(uint64(val.Uint64())) | ||
ty, err = abi.NewType("uint256", "", nil) | ||
case pack.U128: | ||
ethval = val.Int() | ||
ty, err = abi.NewType("uint256", "", nil) | ||
case pack.U256: | ||
ethval = val.Int() | ||
ty, err = abi.NewType("uint256", "", nil) | ||
|
||
case Address: | ||
ethval = val | ||
ty, err = abi.NewType("address", "", nil) | ||
|
||
default: | ||
panic(fmt.Errorf("non-exhaustive pattern: %T", val)) | ||
} | ||
|
||
if err != nil { | ||
panic(fmt.Errorf("error encoding: %v", err)) | ||
} | ||
ethargs = append(ethargs, abi.Argument{ | ||
Type: ty, | ||
}) | ||
ethvals = append(ethvals, ethval) | ||
} | ||
|
||
packed, err := ethargs.Pack(ethvals...) | ||
if err != nil { | ||
panic(fmt.Errorf("error packing: %v", err)) | ||
} | ||
return packed | ||
} |
Oops, something went wrong.