diff --git a/.gitignore b/.gitignore
index 3c697ad..d43a767 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,12 +3,19 @@
*.exe~
*.so
*.dylib
+main
# Test binary, built with `go test -c`
*.test
+test/contracts
+
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
+.DS_Store
# Dependency directories (remove the comment below to include it)
-# vendor/
+vendor/
+
+# ide files
+.vscode/
\ No newline at end of file
diff --git a/README.md b/README.md
index 33a4ff5..37f8aba 100644
--- a/README.md
+++ b/README.md
@@ -2,19 +2,3 @@
使用请参考 [test/main.go](./test/main.go)
-# 安装运行
-1. windoss
-要把./cgofuns/cdll/win下面的所有dll文件与可执行程序放在一起。
-2. linux
-- 版本低于ubuntu-16.04时,把./cgofuns/cdll/linx下面的所有so文件与可执行程序放在一起。
-- 版本等于或者高于ubuntu-16.04,不做其它多余操作。
-3. arm
-- 以下方式暂时方式
-- 打开cgofun/cgo.go文件,删除这一行:#cgo linux LDFLAGS: -Wl,-RPATH="./" -L ./clib/linux/ -lsignature -lboost_regex -lcrypto -lssl -ldl -lstdc++
-- 在相应位置增加这一行:#cgo LDFLAGS: -L ./clib/arm/ -lsignature -lboost_regex -lssl -lcrypto -lstdc++ -ldl
-- CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build
-4. aarch64
-- 以下方式暂时方式
-- 打开cgofun/cgo.go文件,删除这一行:#cgo linux LDFLAGS: -Wl,-RPATH="./" -L ./clib/linux/ -lsignature -lboost_regex -lcrypto -lssl -ldl -lstdc++
-- 在相应位置增加这一行:#cgo LDFLAGS: -L ./clib/aarch64/ -lsignature -lboost_regex -lssl -lcrypto -lstdc++ -ldl
-- CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc go build main.go
diff --git a/abigen/abi/abi.go b/abigen/abi/abi.go
new file mode 100644
index 0000000..98eec49
--- /dev/null
+++ b/abigen/abi/abi.go
@@ -0,0 +1,255 @@
+// Copyright 2015 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 abi
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/crypto"
+)
+
+// The ABI holds information about a contract's context and available
+// invokable methods. It will allow you to type check function calls and
+// packs data accordingly.
+type ABI struct {
+ Constructor Method
+ Methods map[string]Method
+ Events map[string]Event
+ Errors map[string]Error
+
+ // Additional "special" functions introduced in solidity v0.6.0.
+ // It's separated from the original default fallback. Each contract
+ // can only define one fallback and receive function.
+ Fallback Method // Note it's also used to represent legacy fallback before v0.6.0
+ Receive Method
+}
+
+// JSON returns a parsed ABI interface and error if it failed.
+func JSON(reader io.Reader) (ABI, error) {
+ dec := json.NewDecoder(reader)
+
+ var abi ABI
+ if err := dec.Decode(&abi); err != nil {
+ return ABI{}, err
+ }
+ return abi, nil
+}
+
+// Pack the given method name to conform the ABI. Method call's data
+// will consist of method_id, args0, arg1, ... argN. Method id consists
+// of 4 bytes and arguments are all 32 bytes.
+// Method ids are created from the first 4 bytes of the hash of the
+// methods string signature. (signature = baz(uint32,string32))
+func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
+ // Fetch the ABI of the requested method
+ if name == "" {
+ // constructor
+ arguments, err := abi.Constructor.Inputs.Pack(args...)
+ if err != nil {
+ return nil, err
+ }
+ return arguments, nil
+ }
+ method, exist := abi.Methods[name]
+ if !exist {
+ return nil, fmt.Errorf("method '%s' not found", name)
+ }
+ arguments, err := method.Inputs.Pack(args...)
+ if err != nil {
+ return nil, err
+ }
+ // Pack up the method ID too if not a constructor and return
+ return append(method.ID, arguments...), nil
+}
+
+func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
+ // since there can't be naming collisions with contracts and events,
+ // we need to decide whether we're calling a method or an event
+ var args Arguments
+ if method, ok := abi.Methods[name]; ok {
+ if len(data)%32 != 0 {
+ return nil, fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
+ }
+ args = method.Outputs
+ }
+ if event, ok := abi.Events[name]; ok {
+ args = event.Inputs
+ }
+ if args == nil {
+ return nil, errors.New("abi: could not locate named method or event")
+ }
+ return args, nil
+}
+
+// Unpack unpacks the output according to the abi specification.
+func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
+ args, err := abi.getArguments(name, data)
+ if err != nil {
+ return nil, err
+ }
+ return args.Unpack(data)
+}
+
+// UnpackIntoInterface unpacks the output in v according to the abi specification.
+// It performs an additional copy. Please only use, if you want to unpack into a
+// structure that does not strictly conform to the abi structure (e.g. has additional arguments)
+func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error {
+ args, err := abi.getArguments(name, data)
+ if err != nil {
+ return err
+ }
+ unpacked, err := args.Unpack(data)
+ if err != nil {
+ return err
+ }
+ return args.Copy(v, unpacked)
+}
+
+// UnpackIntoMap unpacks a log into the provided map[string]interface{}.
+func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
+ args, err := abi.getArguments(name, data)
+ if err != nil {
+ return err
+ }
+ return args.UnpackIntoMap(v, data)
+}
+
+// UnmarshalJSON implements json.Unmarshaler interface.
+func (abi *ABI) UnmarshalJSON(data []byte) error {
+ var fields []struct {
+ Type string
+ Name string
+ Inputs []Argument
+ Outputs []Argument
+
+ // Status indicator which can be: "pure", "view",
+ // "nonpayable" or "payable".
+ StateMutability string
+
+ // Deprecated Status indicators, but removed in v0.6.0.
+ Constant bool // True if function is either pure or view
+ Payable bool // True if function is payable
+
+ // Event relevant indicator represents the event is
+ // declared as anonymous.
+ Anonymous bool
+ }
+ if err := json.Unmarshal(data, &fields); err != nil {
+ return err
+ }
+ abi.Methods = make(map[string]Method)
+ abi.Events = make(map[string]Event)
+ abi.Errors = make(map[string]Error)
+ for _, field := range fields {
+ switch field.Type {
+ case "constructor":
+ abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
+ case "function":
+ name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
+ abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
+ case "fallback":
+ // New introduced function type in v0.6.0, check more detail
+ // here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
+ if abi.HasFallback() {
+ return errors.New("only single fallback is allowed")
+ }
+ abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
+ case "receive":
+ // New introduced function type in v0.6.0, check more detail
+ // here https://solidity.readthedocs.io/en/v0.6.0/contracts.html#fallback-function
+ if abi.HasReceive() {
+ return errors.New("only single receive is allowed")
+ }
+ if field.StateMutability != "payable" {
+ return errors.New("the statemutability of receive can only be payable")
+ }
+ abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
+ case "event":
+ name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
+ abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
+ case "error":
+ // Errors cannot be overloaded or overridden but are inherited,
+ // no need to resolve the name conflict here.
+ abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
+ default:
+ return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
+ }
+ }
+ return nil
+}
+
+// MethodById looks up a method by the 4-byte id,
+// returns nil if none found.
+func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
+ if len(sigdata) < 4 {
+ return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
+ }
+ for _, method := range abi.Methods {
+ if bytes.Equal(method.ID, sigdata[:4]) {
+ return &method, nil
+ }
+ }
+ return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
+}
+
+// EventByID looks an event up by its topic hash in the
+// ABI and returns nil if none found.
+func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
+ for _, event := range abi.Events {
+ if bytes.Equal(event.ID.Bytes(), topic.Bytes()) {
+ return &event, nil
+ }
+ }
+ return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
+}
+
+// HasFallback returns an indicator whether a fallback function is included.
+func (abi *ABI) HasFallback() bool {
+ return abi.Fallback.Type == Fallback
+}
+
+// HasReceive returns an indicator whether a receive function is included.
+func (abi *ABI) HasReceive() bool {
+ return abi.Receive.Type == Receive
+}
+
+// revertSelector is a special function selector for revert reason unpacking.
+var revertSelector = crypto.Keccak256([]byte("Error(string)"))[:4]
+
+// UnpackRevert resolves the abi-encoded revert reason. According to the solidity
+// spec https://solidity.readthedocs.io/en/latest/control-structures.html#revert,
+// the provided revert reason is abi-encoded as if it were a call to a function
+// `Error(string)`. So it's a special tool for it.
+func UnpackRevert(data []byte) (string, error) {
+ if len(data) < 4 {
+ return "", errors.New("invalid data for unpacking")
+ }
+ if !bytes.Equal(data[:4], revertSelector) {
+ return "", errors.New("invalid data for unpacking")
+ }
+ typ, _ := NewType("string", "", nil)
+ unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
+ if err != nil {
+ return "", err
+ }
+ return unpacked[0].(string), nil
+}
diff --git a/abigen/abi/argument.go b/abigen/abi/argument.go
new file mode 100644
index 0000000..c5326d5
--- /dev/null
+++ b/abigen/abi/argument.go
@@ -0,0 +1,272 @@
+// Copyright 2015 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 abi
+
+import (
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// Argument holds the name of the argument and the corresponding type.
+// Types are used when packing and testing arguments.
+type Argument struct {
+ Name string
+ Type Type
+ Indexed bool // indexed is only used by events
+}
+
+type Arguments []Argument
+
+type ArgumentMarshaling struct {
+ Name string
+ Type string
+ InternalType string
+ Components []ArgumentMarshaling
+ Indexed bool
+}
+
+// UnmarshalJSON implements json.Unmarshaler interface.
+func (argument *Argument) UnmarshalJSON(data []byte) error {
+ var arg ArgumentMarshaling
+ err := json.Unmarshal(data, &arg)
+ if err != nil {
+ return fmt.Errorf("argument json err: %v", err)
+ }
+
+ argument.Type, err = NewType(arg.Type, arg.InternalType, arg.Components)
+ if err != nil {
+ return err
+ }
+ argument.Name = arg.Name
+ argument.Indexed = arg.Indexed
+
+ return nil
+}
+
+// NonIndexed returns the arguments with indexed arguments filtered out.
+func (arguments Arguments) NonIndexed() Arguments {
+ var ret []Argument
+ for _, arg := range arguments {
+ if !arg.Indexed {
+ ret = append(ret, arg)
+ }
+ }
+ return ret
+}
+
+// isTuple returns true for non-atomic constructs, like (uint,uint) or uint[].
+func (arguments Arguments) isTuple() bool {
+ return len(arguments) > 1
+}
+
+// Unpack performs the operation hexdata -> Go format.
+func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
+ if len(data) == 0 {
+ if len(arguments.NonIndexed()) != 0 {
+ return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
+ }
+ return make([]interface{}, 0), nil
+ }
+ return arguments.UnpackValues(data)
+}
+
+// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value.
+func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
+ // Make sure map is not nil
+ if v == nil {
+ return fmt.Errorf("abi: cannot unpack into a nil map")
+ }
+ if len(data) == 0 {
+ if len(arguments.NonIndexed()) != 0 {
+ return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
+ }
+ return nil // Nothing to unmarshal, return
+ }
+ marshalledValues, err := arguments.UnpackValues(data)
+ if err != nil {
+ return err
+ }
+ for i, arg := range arguments.NonIndexed() {
+ v[arg.Name] = marshalledValues[i]
+ }
+ return nil
+}
+
+// Copy performs the operation go format -> provided struct.
+func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
+ // make sure the passed value is arguments pointer
+ if reflect.Ptr != reflect.ValueOf(v).Kind() {
+ return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+ }
+ if len(values) == 0 {
+ if len(arguments.NonIndexed()) != 0 {
+ return fmt.Errorf("abi: attempting to copy no values while arguments are expected")
+ }
+ return nil // Nothing to copy, return
+ }
+ if arguments.isTuple() {
+ return arguments.copyTuple(v, values)
+ }
+ return arguments.copyAtomic(v, values[0])
+}
+
+// unpackAtomic unpacks ( hexdata -> go ) a single value
+func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error {
+ dst := reflect.ValueOf(v).Elem()
+ src := reflect.ValueOf(marshalledValues)
+
+ if dst.Kind() == reflect.Struct {
+ return set(dst.Field(0), src)
+ }
+ return set(dst, src)
+}
+
+// copyTuple copies a batch of values from marshalledValues to v.
+func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface{}) error {
+ value := reflect.ValueOf(v).Elem()
+ nonIndexedArgs := arguments.NonIndexed()
+
+ switch value.Kind() {
+ case reflect.Struct:
+ argNames := make([]string, len(nonIndexedArgs))
+ for i, arg := range nonIndexedArgs {
+ argNames[i] = arg.Name
+ }
+ var err error
+ abi2struct, err := mapArgNamesToStructFields(argNames, value)
+ if err != nil {
+ return err
+ }
+ for i, arg := range nonIndexedArgs {
+ field := value.FieldByName(abi2struct[arg.Name])
+ if !field.IsValid() {
+ return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
+ }
+ if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
+ return err
+ }
+ }
+ case reflect.Slice, reflect.Array:
+ if value.Len() < len(marshalledValues) {
+ return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
+ }
+ for i := range nonIndexedArgs {
+ if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
+ return err
+ }
+ }
+ default:
+ return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
+ }
+ return nil
+}
+
+// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
+// without supplying a struct to unpack into. Instead, this method returns a list containing the
+// values. An atomic argument will be a list with one element.
+func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
+ nonIndexedArgs := arguments.NonIndexed()
+ retval := make([]interface{}, 0, len(nonIndexedArgs))
+ virtualArgs := 0
+ for index, arg := range nonIndexedArgs {
+ marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
+ if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
+ // If we have a static array, like [3]uint256, these are coded as
+ // just like uint256,uint256,uint256.
+ // This means that we need to add two 'virtual' arguments when
+ // we count the index from now on.
+ //
+ // Array values nested multiple levels deep are also encoded inline:
+ // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
+ //
+ // Calculate the full array size to get the correct offset for the next argument.
+ // Decrement it by 1, as the normal index increment is still applied.
+ virtualArgs += getTypeSize(arg.Type)/32 - 1
+ } else if arg.Type.T == TupleTy && !isDynamicType(arg.Type) {
+ // If we have a static tuple, like (uint256, bool, uint256), these are
+ // coded as just like uint256,bool,uint256
+ virtualArgs += getTypeSize(arg.Type)/32 - 1
+ }
+ if err != nil {
+ return nil, err
+ }
+ retval = append(retval, marshalledValue)
+ }
+ return retval, nil
+}
+
+// PackValues performs the operation Go format -> Hexdata.
+// It is the semantic opposite of UnpackValues.
+func (arguments Arguments) PackValues(args []interface{}) ([]byte, error) {
+ return arguments.Pack(args...)
+}
+
+// Pack performs the operation Go format -> Hexdata.
+func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
+ // Make sure arguments match up and pack them
+ abiArgs := arguments
+ if len(args) != len(abiArgs) {
+ return nil, fmt.Errorf("argument count mismatch: got %d for %d", len(args), len(abiArgs))
+ }
+ // variable input is the output appended at the end of packed
+ // output. This is used for strings and bytes types input.
+ var variableInput []byte
+
+ // input offset is the bytes offset for packed output
+ inputOffset := 0
+ for _, abiArg := range abiArgs {
+ inputOffset += getTypeSize(abiArg.Type)
+ }
+ var ret []byte
+ for i, a := range args {
+ input := abiArgs[i]
+ // pack the input
+ packed, err := input.Type.pack(reflect.ValueOf(a))
+ if err != nil {
+ return nil, err
+ }
+ // check for dynamic types
+ if isDynamicType(input.Type) {
+ // set the offset
+ ret = append(ret, packNum(reflect.ValueOf(inputOffset))...)
+ // calculate next offset
+ inputOffset += len(packed)
+ // append to variable input
+ variableInput = append(variableInput, packed...)
+ } else {
+ // append the packed value to the input
+ ret = append(ret, packed...)
+ }
+ }
+ // append the variable input at the end of the packed input
+ ret = append(ret, variableInput...)
+
+ return ret, nil
+}
+
+// ToCamelCase converts an under-score string to a camel-case string
+func ToCamelCase(input string) string {
+ parts := strings.Split(input, "_")
+ for i, s := range parts {
+ if len(s) > 0 {
+ parts[i] = strings.ToUpper(s[:1]) + s[1:]
+ }
+ }
+ return strings.Join(parts, "")
+}
diff --git a/abigen/abi/bind/backend.go b/abigen/abi/bind/backend.go
new file mode 100644
index 0000000..56c3495
--- /dev/null
+++ b/abigen/abi/bind/backend.go
@@ -0,0 +1,124 @@
+// Copyright 2015 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 bind
+
+import (
+ "context"
+ "errors"
+ "math/big"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+)
+
+var (
+ // ErrNoCode is returned by call and transact operations for which the requested
+ // recipient contract to operate on does not exist in the state db or does not
+ // have any code associated with it (i.e. suicided).
+ ErrNoCode = errors.New("no contract code at given address")
+
+ // ErrNoPendingState is raised when attempting to perform a pending state action
+ // on a backend that doesn't implement PendingContractCaller.
+ ErrNoPendingState = errors.New("backend does not support pending state")
+
+ // ErrNoCodeAfterDeploy is returned by WaitDeployed if contract creation leaves
+ // an empty contract behind.
+ ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
+)
+
+// ContractCaller defines the methods needed to allow operating with a contract on a read
+// only basis.
+type ContractCaller interface {
+ // CodeAt returns the code of the given account. This is needed to differentiate
+ // between contract internal errors and the local chain being out of sync.
+ CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
+
+ // CallContract executes an Ethereum contract call with the specified data as the
+ // input.
+ // CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
+}
+
+// PendingContractCaller defines methods to perform contract calls on the pending state.
+// Call will try to discover this interface when access to the pending state is requested.
+// If the backend does not support the pending state, Call returns ErrNoPendingState.
+type PendingContractCaller interface {
+ // PendingCodeAt returns the code of the given account in the pending state.
+ PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
+
+ // PendingCallContract executes an Ethereum contract call against the pending state.
+ // PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
+}
+
+// ContractTransactor defines the methods needed to allow operating with a contract
+// on a write only basis. Besides the transacting method, the remainder are helpers
+// used when the user does not provide some needed values, but rather leaves it up
+// to the transactor to decide.
+type ContractTransactor interface {
+ // HeaderByNumber returns a block header from the current canonical chain. If
+ // number is nil, the latest known header is returned.
+ // HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
+
+ // PendingCodeAt returns the code of the given account in the pending state.
+ PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
+
+ // PendingNonceAt retrieves the current pending nonce associated with an account.
+ PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
+
+ // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
+ // execution of a transaction.
+ SuggestGasPrice(ctx context.Context) (*big.Int, error)
+
+ // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
+ // a timely execution of a transaction.
+ SuggestGasTipCap(ctx context.Context) (*big.Int, error)
+
+ // EstimateGas tries to estimate the gas needed to execute a specific
+ // transaction based on the current pending state of the backend blockchain.
+ // There is no guarantee that this is the true gas limit requirement as other
+ // transactions may be added or removed by miners, but it should provide a basis
+ // for setting a reasonable default.
+ // EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)
+
+ // SendTransaction injects the transaction into the pending pool for execution.
+ // SendTransaction(ctx context.Context, tx *types.Transaction) error
+}
+
+// ContractFilterer defines the methods needed to access log events using one-off
+// queries or continuous event subscriptions.
+type ContractFilterer interface {
+ // FilterLogs executes a log filter operation, blocking during execution and
+ // returning all the results in one batch.
+ //
+ // TODO(karalabe): Deprecate when the subscription one can return past data too.
+ // FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error)
+
+ // SubscribeFilterLogs creates a background log filtering operation, returning
+ // a subscription immediately, which can be used to stream the found events.
+ // SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
+}
+
+// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
+type DeployBackend interface {
+ // TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
+ CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
+}
+
+// ContractBackend defines the methods needed to work with contracts on a read-write basis.
+type ContractBackend interface {
+ ContractCaller
+ ContractTransactor
+ ContractFilterer
+}
diff --git a/abigen/abi/bind/bind.go b/abigen/abi/bind/bind.go
new file mode 100644
index 0000000..a932ff9
--- /dev/null
+++ b/abigen/abi/bind/bind.go
@@ -0,0 +1,612 @@
+// Copyright 2016 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 bind generates Ethereum contract Go bindings.
+//
+// Detailed usage document and tutorial available on the go-ethereum Wiki page:
+// https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
+package bind
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/format"
+ "log"
+ "regexp"
+ "strings"
+ "text/template"
+ "unicode"
+
+ "github.com/ChainSQL/go-chainsql-api/abigen/abi"
+)
+
+// Lang is a target programming language selector to generate bindings for.
+type Lang int
+
+const (
+ LangGo Lang = iota
+ LangJava
+ LangObjC
+)
+
+// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
+// to be used as is in client code, but rather as an intermediate struct which
+// enforces compile time type safety and naming convention opposed to having to
+// manually maintain hard coded strings that break on runtime.
+func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
+ var (
+ // contracts is the map of each individual contract requested binding
+ contracts = make(map[string]*tmplContract)
+
+ // structs is the map of all redeclared structs shared by passed contracts.
+ structs = make(map[string]*tmplStruct)
+
+ // isLib is the map used to flag each encountered library as such
+ isLib = make(map[string]struct{})
+ )
+ for i := 0; i < len(types); i++ {
+ // Parse the actual ABI to generate the binding for
+ evmABI, err := abi.JSON(strings.NewReader(abis[i]))
+ if err != nil {
+ return "", err
+ }
+ // Strip any whitespace from the JSON ABI
+ strippedABI := strings.Map(func(r rune) rune {
+ if unicode.IsSpace(r) {
+ return -1
+ }
+ return r
+ }, abis[i])
+
+ // Extract the call and transact methods; events, struct definitions; and sort them alphabetically
+ var (
+ calls = make(map[string]*tmplMethod)
+ transacts = make(map[string]*tmplMethod)
+ events = make(map[string]*tmplEvent)
+ fallback *tmplMethod
+ receive *tmplMethod
+
+ // identifiers are used to detect duplicated identifiers of functions
+ // and events. For all calls, transacts and events, abigen will generate
+ // corresponding bindings. However we have to ensure there is no
+ // identifier collisions in the bindings of these categories.
+ callIdentifiers = make(map[string]bool)
+ transactIdentifiers = make(map[string]bool)
+ eventIdentifiers = make(map[string]bool)
+ )
+
+ for _, input := range evmABI.Constructor.Inputs {
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+
+ for _, original := range evmABI.Methods {
+ // Normalize the method for capital cases and non-anonymous inputs/outputs
+ normalized := original
+ normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
+
+ // Ensure there is no duplicated identifier
+ var identifiers = callIdentifiers
+ if !original.IsConstant() {
+ identifiers = transactIdentifiers
+ }
+ if identifiers[normalizedName] {
+ return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
+ }
+ identifiers[normalizedName] = true
+
+ normalized.Name = normalizedName
+ normalized.Inputs = make([]abi.Argument, len(original.Inputs))
+ copy(normalized.Inputs, original.Inputs)
+ for j, input := range normalized.Inputs {
+ if input.Name == "" {
+ normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
+ }
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+ normalized.Outputs = make([]abi.Argument, len(original.Outputs))
+ copy(normalized.Outputs, original.Outputs)
+ for j, output := range normalized.Outputs {
+ if output.Name != "" {
+ normalized.Outputs[j].Name = capitalise(output.Name)
+ }
+ if hasStruct(output.Type) {
+ bindStructType[lang](output.Type, structs)
+ }
+ }
+ // Append the methods to the call or transact lists
+ if original.IsConstant() {
+ calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
+ } else {
+ transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
+ }
+ }
+ for _, original := range evmABI.Events {
+ // Skip anonymous events as they don't support explicit filtering
+ if original.Anonymous {
+ continue
+ }
+ // Normalize the event for capital cases and non-anonymous outputs
+ normalized := original
+
+ // Ensure there is no duplicated identifier
+ normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
+ if eventIdentifiers[normalizedName] {
+ return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
+ }
+ eventIdentifiers[normalizedName] = true
+ normalized.Name = normalizedName
+
+ used := make(map[string]bool)
+ normalized.Inputs = make([]abi.Argument, len(original.Inputs))
+ copy(normalized.Inputs, original.Inputs)
+ for j, input := range normalized.Inputs {
+ if input.Name == "" {
+ normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
+ }
+ // Event is a bit special, we need to define event struct in binding,
+ // ensure there is no camel-case-style name conflict.
+ for index := 0; ; index++ {
+ if !used[capitalise(normalized.Inputs[j].Name)] {
+ used[capitalise(normalized.Inputs[j].Name)] = true
+ break
+ }
+ normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
+ }
+ if hasStruct(input.Type) {
+ bindStructType[lang](input.Type, structs)
+ }
+ }
+ // Append the event to the accumulator list
+ events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
+ }
+ // Add two special fallback functions if they exist
+ if evmABI.HasFallback() {
+ fallback = &tmplMethod{Original: evmABI.Fallback}
+ }
+ if evmABI.HasReceive() {
+ receive = &tmplMethod{Original: evmABI.Receive}
+ }
+ // There is no easy way to pass arbitrary java objects to the Go side.
+ if len(structs) > 0 && lang == LangJava {
+ return "", errors.New("java binding for tuple arguments is not supported yet")
+ }
+
+ contracts[types[i]] = &tmplContract{
+ Type: capitalise(types[i]),
+ InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
+ InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
+ Constructor: evmABI.Constructor,
+ Calls: calls,
+ Transacts: transacts,
+ Fallback: fallback,
+ Receive: receive,
+ Events: events,
+ Libraries: make(map[string]string),
+ }
+ // Function 4-byte signatures are stored in the same sequence
+ // as types, if available.
+ if len(fsigs) > i {
+ contracts[types[i]].FuncSigs = fsigs[i]
+ }
+ // Parse library references.
+ for pattern, name := range libs {
+ matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
+ if err != nil {
+ log.Printf("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
+ }
+ if matched {
+ contracts[types[i]].Libraries[pattern] = name
+ // keep track that this type is a library
+ if _, ok := isLib[name]; !ok {
+ isLib[name] = struct{}{}
+ }
+ }
+ }
+ }
+ // Check if that type has already been identified as a library
+ for i := 0; i < len(types); i++ {
+ _, ok := isLib[types[i]]
+ contracts[types[i]].Library = ok
+ }
+ // Generate the contract template data content and render it
+ data := &tmplData{
+ Package: pkg,
+ Contracts: contracts,
+ Libraries: libs,
+ Structs: structs,
+ }
+ buffer := new(bytes.Buffer)
+
+ funcs := map[string]interface{}{
+ "bindtype": bindType[lang],
+ "bindtopictype": bindTopicType[lang],
+ "namedtype": namedType[lang],
+ "capitalise": capitalise,
+ "decapitalise": decapitalise,
+ }
+ tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
+ if err := tmpl.Execute(buffer, data); err != nil {
+ return "", err
+ }
+ // For Go bindings pass the code through gofmt to clean it up
+ if lang == LangGo {
+ code, err := format.Source(buffer.Bytes())
+ if err != nil {
+ return "", fmt.Errorf("%v\n%s", err, buffer)
+ }
+ return string(code), nil
+ }
+ // For all others just return as is for now
+ return buffer.String(), nil
+}
+
+// bindType is a set of type binders that convert Solidity types to some supported
+// programming language types.
+var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindTypeGo,
+ LangJava: bindTypeJava,
+}
+
+// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
+func bindBasicTypeGo(kind abi.Type) string {
+ switch kind.T {
+ case abi.AddressTy:
+ return "common.Address"
+ case abi.IntTy, abi.UintTy:
+ parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ return fmt.Sprintf("%sint%s", parts[1], parts[2])
+ }
+ return "*big.Int"
+ case abi.FixedBytesTy:
+ return fmt.Sprintf("[%d]byte", kind.Size)
+ case abi.BytesTy:
+ return "[]byte"
+ case abi.FunctionTy:
+ return "[24]byte"
+ default:
+ // string, bool types
+ return kind.String()
+ }
+}
+
+// bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
+// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
+// mapped will use an upscaled type (e.g. BigDecimal).
+func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ return structs[kind.TupleRawName+kind.String()].Name
+ case abi.ArrayTy:
+ return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
+ case abi.SliceTy:
+ return "[]" + bindTypeGo(*kind.Elem, structs)
+ default:
+ return bindBasicTypeGo(kind)
+ }
+}
+
+// bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java ones.
+func bindBasicTypeJava(kind abi.Type) string {
+ switch kind.T {
+ case abi.AddressTy:
+ return "Address"
+ case abi.IntTy, abi.UintTy:
+ // Note that uint and int (without digits) are also matched,
+ // these are size 256, and will translate to BigInt (the default).
+ parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
+ if len(parts) != 3 {
+ return kind.String()
+ }
+ // All unsigned integers should be translated to BigInt since gomobile doesn't
+ // support them.
+ if parts[1] == "u" {
+ return "BigInt"
+ }
+
+ namedSize := map[string]string{
+ "8": "byte",
+ "16": "short",
+ "32": "int",
+ "64": "long",
+ }[parts[2]]
+
+ // default to BigInt
+ if namedSize == "" {
+ namedSize = "BigInt"
+ }
+ return namedSize
+ case abi.FixedBytesTy, abi.BytesTy:
+ return "byte[]"
+ case abi.BoolTy:
+ return "boolean"
+ case abi.StringTy:
+ return "String"
+ case abi.FunctionTy:
+ return "byte[24]"
+ default:
+ return kind.String()
+ }
+}
+
+// pluralizeJavaType explicitly converts multidimensional types to predefined
+// types in go side.
+func pluralizeJavaType(typ string) string {
+ switch typ {
+ case "boolean":
+ return "Bools"
+ case "String":
+ return "Strings"
+ case "Address":
+ return "Addresses"
+ case "byte[]":
+ return "Binaries"
+ case "BigInt":
+ return "BigInts"
+ }
+ return typ + "[]"
+}
+
+// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
+// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
+// mapped will use an upscaled type (e.g. BigDecimal).
+func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ return structs[kind.TupleRawName+kind.String()].Name
+ case abi.ArrayTy, abi.SliceTy:
+ return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
+ default:
+ return bindBasicTypeJava(kind)
+ }
+}
+
+// bindTopicType is a set of type binders that convert Solidity types to some
+// supported programming language topic types.
+var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindTopicTypeGo,
+ LangJava: bindTopicTypeJava,
+}
+
+// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
+// functionality as for simple types, but dynamic types get converted to hashes.
+func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ bound := bindTypeGo(kind, structs)
+
+ // todo(rjl493456442) according solidity documentation, indexed event
+ // parameters that are not value types i.e. arrays and structs are not
+ // stored directly but instead a keccak256-hash of an encoding is stored.
+ //
+ // We only convert stringS and bytes to hash, still need to deal with
+ // array(both fixed-size and dynamic-size) and struct.
+ if bound == "string" || bound == "[]byte" {
+ bound = "common.Hash"
+ }
+ return bound
+}
+
+// bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same
+// functionality as for simple types, but dynamic types get converted to hashes.
+func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ bound := bindTypeJava(kind, structs)
+
+ // todo(rjl493456442) according solidity documentation, indexed event
+ // parameters that are not value types i.e. arrays and structs are not
+ // stored directly but instead a keccak256-hash of an encoding is stored.
+ //
+ // We only convert strings and bytes to hash, still need to deal with
+ // array(both fixed-size and dynamic-size) and struct.
+ if bound == "String" || bound == "byte[]" {
+ bound = "Hash"
+ }
+ return bound
+}
+
+// bindStructType is a set of type binders that convert Solidity tuple types to some supported
+// programming language struct definition.
+var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
+ LangGo: bindStructTypeGo,
+ LangJava: bindStructTypeJava,
+}
+
+// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
+// in the given map.
+// Notably, this function will resolve and record nested struct recursively.
+func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ // We compose a raw struct name and a canonical parameter expression
+ // together here. The reason is before solidity v0.5.11, kind.TupleRawName
+ // is empty, so we use canonical parameter expression to distinguish
+ // different struct definition. From the consideration of backward
+ // compatibility, we concat these two together so that if kind.TupleRawName
+ // is not empty, it can have unique id.
+ id := kind.TupleRawName + kind.String()
+ if s, exist := structs[id]; exist {
+ return s.Name
+ }
+ var (
+ names = make(map[string]bool)
+ fields []*tmplField
+ )
+ for i, elem := range kind.TupleElems {
+ name := capitalise(kind.TupleRawNames[i])
+ name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
+ names[name] = true
+ fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
+ }
+ name := kind.TupleRawName
+ if name == "" {
+ name = fmt.Sprintf("Struct%d", len(structs))
+ }
+ name = capitalise(name)
+
+ structs[id] = &tmplStruct{
+ Name: name,
+ Fields: fields,
+ }
+ return name
+ case abi.ArrayTy:
+ return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
+ case abi.SliceTy:
+ return "[]" + bindStructTypeGo(*kind.Elem, structs)
+ default:
+ return bindBasicTypeGo(kind)
+ }
+}
+
+// bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping
+// in the given map.
+// Notably, this function will resolve and record nested struct recursively.
+func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
+ switch kind.T {
+ case abi.TupleTy:
+ // We compose a raw struct name and a canonical parameter expression
+ // together here. The reason is before solidity v0.5.11, kind.TupleRawName
+ // is empty, so we use canonical parameter expression to distinguish
+ // different struct definition. From the consideration of backward
+ // compatibility, we concat these two together so that if kind.TupleRawName
+ // is not empty, it can have unique id.
+ id := kind.TupleRawName + kind.String()
+ if s, exist := structs[id]; exist {
+ return s.Name
+ }
+ var fields []*tmplField
+ for i, elem := range kind.TupleElems {
+ field := bindStructTypeJava(*elem, structs)
+ fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
+ }
+ name := kind.TupleRawName
+ if name == "" {
+ name = fmt.Sprintf("Class%d", len(structs))
+ }
+ structs[id] = &tmplStruct{
+ Name: name,
+ Fields: fields,
+ }
+ return name
+ case abi.ArrayTy, abi.SliceTy:
+ return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs))
+ default:
+ return bindBasicTypeJava(kind)
+ }
+}
+
+// namedType is a set of functions that transform language specific types to
+// named versions that may be used inside method names.
+var namedType = map[Lang]func(string, abi.Type) string{
+ LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
+ LangJava: namedTypeJava,
+}
+
+// namedTypeJava converts some primitive data types to named variants that can
+// be used as parts of method names.
+func namedTypeJava(javaKind string, solKind abi.Type) string {
+ switch javaKind {
+ case "byte[]":
+ return "Binary"
+ case "boolean":
+ return "Bool"
+ default:
+ parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
+ if len(parts) != 4 {
+ return javaKind
+ }
+ switch parts[2] {
+ case "8", "16", "32", "64":
+ if parts[3] == "" {
+ return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
+ }
+ return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
+
+ default:
+ return javaKind
+ }
+ }
+}
+
+// alias returns an alias of the given string based on the aliasing rules
+// or returns itself if no rule is matched.
+func alias(aliases map[string]string, n string) string {
+ if alias, exist := aliases[n]; exist {
+ return alias
+ }
+ return n
+}
+
+// methodNormalizer is a name transformer that modifies Solidity method names to
+// conform to target language naming conventions.
+var methodNormalizer = map[Lang]func(string) string{
+ LangGo: abi.ToCamelCase,
+ LangJava: decapitalise,
+}
+
+// capitalise makes a camel-case string which starts with an upper case character.
+var capitalise = abi.ToCamelCase
+
+// decapitalise makes a camel-case string which starts with a lower case character.
+func decapitalise(input string) string {
+ if len(input) == 0 {
+ return input
+ }
+
+ goForm := abi.ToCamelCase(input)
+ return strings.ToLower(goForm[:1]) + goForm[1:]
+}
+
+// structured checks whether a list of ABI data types has enough information to
+// operate through a proper Go struct or if flat returns are needed.
+func structured(args abi.Arguments) bool {
+ if len(args) < 2 {
+ return false
+ }
+ exists := make(map[string]bool)
+ for _, out := range args {
+ // If the name is anonymous, we can't organize into a struct
+ if out.Name == "" {
+ return false
+ }
+ // If the field name is empty when normalized or collides (var, Var, _var, _Var),
+ // we can't organize into a struct
+ field := capitalise(out.Name)
+ if field == "" || exists[field] {
+ return false
+ }
+ exists[field] = true
+ }
+ return true
+}
+
+// hasStruct returns an indicator whether the given type is struct, struct slice
+// or struct array.
+func hasStruct(t abi.Type) bool {
+ switch t.T {
+ case abi.SliceTy:
+ return hasStruct(*t.Elem)
+ case abi.ArrayTy:
+ return hasStruct(*t.Elem)
+ case abi.TupleTy:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/abigen/abi/bind/template.go b/abigen/abi/bind/template.go
new file mode 100644
index 0000000..b53ea14
--- /dev/null
+++ b/abigen/abi/bind/template.go
@@ -0,0 +1,732 @@
+// Copyright 2016 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 bind
+
+import "github.com/ChainSQL/go-chainsql-api/abigen/abi"
+
+// tmplData is the data structure required to fill the binding template.
+type tmplData struct {
+ Package string // Name of the package to place the generated file in
+ Contracts map[string]*tmplContract // List of contracts to generate into this file
+ Libraries map[string]string // Map the bytecode's link pattern to the library name
+ Structs map[string]*tmplStruct // Contract struct type definitions
+}
+
+// tmplContract contains the data needed to generate an individual contract binding.
+type tmplContract struct {
+ Type string // Type name of the main contract binding
+ InputABI string // JSON ABI used as the input to generate the binding from
+ InputBin string // Optional EVM bytecode used to generate deploy code from
+ FuncSigs map[string]string // Optional map: string signature -> 4-byte signature
+ Constructor abi.Method // Contract constructor for deploy parametrization
+ Calls map[string]*tmplMethod // Contract calls that only read state data
+ Transacts map[string]*tmplMethod // Contract calls that write state data
+ Fallback *tmplMethod // Additional special fallback function
+ Receive *tmplMethod // Additional special receive function
+ Events map[string]*tmplEvent // Contract events accessors
+ Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
+ Library bool // Indicator whether the contract is a library
+}
+
+// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
+// and cached data fields.
+type tmplMethod struct {
+ Original abi.Method // Original method as parsed by the abi package
+ Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns)
+ Structured bool // Whether the returns should be accumulated into a struct
+}
+
+// tmplEvent is a wrapper around an abi.Event that contains a few preprocessed
+// and cached data fields.
+type tmplEvent struct {
+ Original abi.Event // Original event as parsed by the abi package
+ Normalized abi.Event // Normalized version of the parsed fields
+}
+
+// tmplField is a wrapper around a struct field with binding language
+// struct type definition and relative filed name.
+type tmplField struct {
+ Type string // Field type representation depends on target binding language
+ Name string // Field name converted from the raw user-defined field name
+ SolKind abi.Type // Raw abi type information
+}
+
+// tmplStruct is a wrapper around an abi.tuple and contains an auto-generated
+// struct name.
+type tmplStruct struct {
+ Name string // Auto-generated struct name(before solidity v0.5.11) or raw name.
+ Fields []*tmplField // Struct fields definition depends on the binding language.
+}
+
+// tmplSource is language to template mapping containing all the supported
+// programming languages the package can generate to.
+var tmplSource = map[Lang]string{
+ LangGo: tmplSourceGo,
+ LangJava: tmplSourceJava,
+}
+
+// tmplSourceGo is the Go source template that the generated Go contract binding
+// is based on.
+const tmplSourceGo = `
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package {{.Package}}
+
+import (
+ "errors"
+ "math/big"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/abigen/abi"
+ "github.com/ChainSQL/go-chainsql-api/abigen/abi/bind"
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/core"
+ "github.com/ChainSQL/go-chainsql-api/data"
+ "github.com/ChainSQL/go-chainsql-api/event"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var (
+ _ = errors.New
+ _ = big.NewInt
+ _ = strings.NewReader
+ _ = bind.Bind
+ _ = common.Big1
+)
+
+{{$structs := .Structs}}
+{{range $structs}}
+ // {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
+ type {{.Name}} struct {
+ {{range $field := .Fields}}
+ {{$field.Name}} {{$field.Type}}{{end}}
+ }
+{{end}}
+
+{{range $contract := .Contracts}}
+ // {{.Type}}MetaData contains all meta data concerning the {{.Type}} contract.
+ var {{.Type}}MetaData = &core.CtrMetaData{
+ ABI: "{{.InputABI}}",
+ {{if $contract.FuncSigs -}}
+ Sigs: map[string]string{
+ {{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
+ {{end}}
+ },
+ {{end -}}
+ {{if .InputBin -}}
+ Bin: "0x{{.InputBin}}",
+ {{end}}
+ }
+ // {{.Type}}ABI is the input ABI used to generate the binding from.
+ // Deprecated: Use {{.Type}}MetaData.ABI instead.
+ var {{.Type}}ABI = {{.Type}}MetaData.ABI
+
+ {{if $contract.FuncSigs}}
+ // Deprecated: Use {{.Type}}MetaData.Sigs instead.
+ // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
+ var {{.Type}}FuncSigs = {{.Type}}MetaData.Sigs
+ {{end}}
+
+ {{if .InputBin}}
+ // {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
+ // Deprecated: Use {{.Type}}MetaData.Bin instead.
+ var {{.Type}}Bin = {{.Type}}MetaData.Bin
+
+ // Deploy{{.Type}} deploys a new ChainSQL contract, binding an instance of {{.Type}} to it.
+ func Deploy{{.Type}}(chainsql *core.Chainsql, auth *core.TransactOpts {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (*core.DeployTxRet, *{{.Type}}, error) {
+ parsed, err := {{.Type}}MetaData.GetAbi()
+ if err != nil {
+ return &core.DeployTxRet{}, nil, err
+ }
+ if parsed == nil {
+ return &core.DeployTxRet{}, nil, errors.New("GetABI returned nil")
+ }
+ {{range $pattern, $name := .Libraries}}
+ {{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(chainsql, auth)
+ {{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
+ {{end}}
+ deployRet, contract, err := core.DeployContract(chainsql, auth, *parsed, common.FromHex({{.Type}}Bin) {{range .Constructor.Inputs}}, {{.Name}}{{end}})
+ if err != nil {
+ return &core.DeployTxRet{}, nil, err
+ }
+ return deployRet, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
+ }
+ {{end}}
+
+ // {{.Type}} is an auto generated Go binding around an ChainSQL contract.
+ type {{.Type}} struct {
+ {{.Type}}Caller // Read-only binding to the contract
+ {{.Type}}Transactor // Write-only binding to the contract
+ {{.Type}}Filterer // Log filterer for contract events
+ }
+
+ // {{.Type}}Caller is an auto generated read-only Go binding around an ChainSQL contract.
+ type {{.Type}}Caller struct {
+ contract *core.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Transactor is an auto generated write-only Go binding around an ChainSQL contract.
+ type {{.Type}}Transactor struct {
+ contract *core.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Filterer is an auto generated log filtering Go binding around an ChainSQL contract events.
+ type {{.Type}}Filterer struct {
+ contract *core.BoundContract // Generic contract wrapper for the low level calls
+ }
+
+ // {{.Type}}Session is an auto generated Go binding around an ChainSQL contract,
+ // with pre-set call and transact options.
+ type {{.Type}}Session struct {
+ Contract *{{.Type}} // Generic contract binding to set the session for
+ CallOpts core.CallOpts // Call options to use throughout this session
+ TransactOpts core.TransactOpts // Transaction auth options to use throughout this session
+ }
+
+ // {{.Type}}CallerSession is an auto generated read-only Go binding around an ChainSQL contract,
+ // with pre-set call options.
+ type {{.Type}}CallerSession struct {
+ Contract *{{.Type}}Caller // Generic contract caller binding to set the session for
+ CallOpts core.CallOpts // Call options to use throughout this session
+ }
+
+ // {{.Type}}TransactorSession is an auto generated write-only Go binding around an ChainSQL contract,
+ // with pre-set transact options.
+ type {{.Type}}TransactorSession struct {
+ Contract *{{.Type}}Transactor // Generic contract transactor binding to set the session for
+ TransactOpts core.TransactOpts // Transaction auth options to use throughout this session
+ }
+
+ // {{.Type}}Raw is an auto generated low-level Go binding around an ChainSQL contract.
+ type {{.Type}}Raw struct {
+ Contract *{{.Type}} // Generic contract binding to access the raw methods on
+ }
+
+ // {{.Type}}CallerRaw is an auto generated low-level read-only Go binding around an ChainSQL contract.
+ type {{.Type}}CallerRaw struct {
+ Contract *{{.Type}}Caller // Generic read-only contract binding to access the raw methods on
+ }
+
+ // {{.Type}}TransactorRaw is an auto generated low-level write-only Go binding around an ChainSQL contract.
+ type {{.Type}}TransactorRaw struct {
+ Contract *{{.Type}}Transactor // Generic write-only contract binding to access the raw methods on
+ }
+
+ // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
+ func New{{.Type}}(chainsql *core.Chainsql, address string) (*{{.Type}}, error) {
+ contract, err := bind{{.Type}}(chainsql, address)
+ if err != nil {
+ return nil, err
+ }
+ return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
+ }
+
+ // // New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
+ // func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
+ // contract, err := bind{{.Type}}(address, caller, nil, nil)
+ // if err != nil {
+ // return nil, err
+ // }
+ // return &{{.Type}}Caller{contract: contract}, nil
+ // }
+
+ // // New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
+ // func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
+ // contract, err := bind{{.Type}}(address, nil, transactor, nil)
+ // if err != nil {
+ // return nil, err
+ // }
+ // return &{{.Type}}Transactor{contract: contract}, nil
+ // }
+
+ // // New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
+ // func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
+ // contract, err := bind{{.Type}}(address, nil, nil, filterer)
+ // if err != nil {
+ // return nil, err
+ // }
+ // return &{{.Type}}Filterer{contract: contract}, nil
+ // }
+
+ // bind{{.Type}} binds a generic wrapper to an already deployed contract.
+ func bind{{.Type}}(chainsql *core.Chainsql, address string) (*core.BoundContract, error) {
+ parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
+ if err != nil {
+ return nil, err
+ }
+ return core.NewBoundContract(chainsql, address, parsed), nil
+ }
+
+ // Call invokes the (constant) contract method with params as input values and
+ // sets the output to result. The result type might be a single field for simple
+ // returns, a slice of interfaces for anonymous returns and a struct for named
+ // returns.
+ // func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ // return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...)
+ // }
+
+ // Transfer initiates a plain transaction to move funds to the contract, calling
+ // its default method if one is available.
+ // func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ // return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transfer(opts)
+ // }
+
+ // Transact invokes the (paid) contract method with params as input values.
+ // func (_{{$contract.Type}} *{{$contract.Type}}Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ // return _{{$contract.Type}}.Contract.{{$contract.Type}}Transactor.contract.Transact(opts, method, params...)
+ // }
+
+ // Call invokes the (constant) contract method with params as input values and
+ // sets the output to result. The result type might be a single field for simple
+ // returns, a slice of interfaces for anonymous returns and a struct for named
+ // returns.
+ // func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *core.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ // return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...)
+ // }
+
+ // Transfer initiates a plain transaction to move funds to the contract, calling
+ // its default method if one is available.
+ // func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transfer(opts *core.TransactOpts) (*types.Transaction, error) {
+ // return _{{$contract.Type}}.Contract.contract.Transfer(opts)
+ // }
+
+ // Transact invokes the (paid) contract method with params as input values.
+ // func (_{{$contract.Type}} *{{$contract.Type}}TransactorRaw) Transact(opts *core.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ // return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
+ // }
+
+ {{range .Calls}}
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *core.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
+ var out []interface{}
+ err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ {{if .Structured}}
+ outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
+ if err != nil {
+ return *outstruct, err
+ }
+ {{range $i, $t := .Normalized.Outputs}}
+ outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
+
+ return *outstruct, err
+ {{else}}
+ if err != nil {
+ return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
+ }
+ {{range $i, $t := .Normalized.Outputs}}
+ out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
+
+ return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err
+ {{end}}
+ }
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+ {{end}}
+
+ {{range .Transacts}}
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *core.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*common.TxResult, error) {
+ return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*common.TxResult, error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*common.TxResult, error) {
+ return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
+ }
+ {{end}}
+
+ {{if .Fallback}}
+ // // Fallback is a paid mutator transaction binding the contract fallback function.
+ // //
+ // // Solidity: {{.Fallback.Original.String}}
+ // func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Fallback(opts *core.TransactOpts, calldata []byte) (*types.Transaction, error) {
+ // return _{{$contract.Type}}.contract.RawTransact(opts, calldata)
+ // }
+
+ // // Fallback is a paid mutator transaction binding the contract fallback function.
+ // //
+ // // Solidity: {{.Fallback.Original.String}}
+ // func (_{{$contract.Type}} *{{$contract.Type}}Session) Fallback(calldata []byte) (*types.Transaction, error) {
+ // return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
+ // }
+
+ // // Fallback is a paid mutator transaction binding the contract fallback function.
+ // //
+ // // Solidity: {{.Fallback.Original.String}}
+ // func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Fallback(calldata []byte) (*types.Transaction, error) {
+ // return _{{$contract.Type}}.Contract.Fallback(&_{{$contract.Type}}.TransactOpts, calldata)
+ // }
+ {{end}}
+
+ {{if .Receive}}
+ // // Receive is a paid mutator transaction binding the contract receive function.
+ // //
+ // // Solidity: {{.Receive.Original.String}}
+ // func (_{{$contract.Type}} *{{$contract.Type}}Transactor) Receive(opts *core.TransactOpts) (*types.Transaction, error) {
+ // return _{{$contract.Type}}.contract.RawTransact(opts, nil) // calldata is disallowed for receive function
+ // }
+
+ // // Receive is a paid mutator transaction binding the contract receive function.
+ // //
+ // // Solidity: {{.Receive.Original.String}}
+ // func (_{{$contract.Type}} *{{$contract.Type}}Session) Receive() (*types.Transaction, error) {
+ // return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
+ // }
+
+ // // Receive is a paid mutator transaction binding the contract receive function.
+ // //
+ // // Solidity: {{.Receive.Original.String}}
+ // func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) Receive() (*types.Transaction, error) {
+ // return _{{$contract.Type}}.Contract.Receive(&_{{$contract.Type}}.TransactOpts)
+ // }
+ {{end}}
+
+ {{range .Events}}
+ // {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
+ type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
+ Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
+
+ contract *core.BoundContract // Generic contract to use for unpacking event data
+ event string // Event name to use for unpacking event data
+
+ logs chan data.Log // Log channel receiving the found contract events
+ sub event.Subscription // Subscription for errors, completion and termination
+ done bool // Whether the subscription completed delivering logs
+ fail error // Occurred error to stop iteration
+ }
+ // Next advances the iterator to the subsequent event, returning whether there
+ // are any more events found. In case of a retrieval or parsing error, false is
+ // returned and Error() can be queried for the exact failure.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
+ // If the iterator failed, stop iterating
+ if (it.fail != nil) {
+ return false
+ }
+ // If the iterator completed, deliver directly whatever's available
+ if (it.done) {
+ select {
+ case log := <-it.logs:
+ it.Event = new({{$contract.Type}}{{.Normalized.Name}})
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+ // Iterator still in progress, wait for either a data or an error event
+ select {
+ case log := <-it.logs:
+ it.Event = new({{$contract.Type}}{{.Normalized.Name}})
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+ }
+ // Error returns any retrieval or parsing error occurred during filtering.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
+ return it.fail
+ }
+ // Close terminates the iteration process, releasing any pending underlying
+ // resources.
+ func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+ }
+
+ // {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
+ type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
+ {{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type $structs}}{{else}}{{bindtype .Type $structs}}{{end}}; {{end}}
+ Raw data.Log // Blockchain specific contextual infos
+ }
+
+ // Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ // func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *core.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
+ // {{range .Normalized.Inputs}}
+ // {{if .Indexed}}var {{.Name}}Rule []interface{}
+ // for _, {{.Name}}Item := range {{.Name}} {
+ // {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
+ // }{{end}}{{end}}
+
+ // logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
+ // if err != nil {
+ // return nil, err
+ // }
+ // return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
+ // }
+
+ // Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *core.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
+ {{range .Normalized.Inputs}}
+ {{if .Indexed}}var {{.Name}}Rule []interface{}
+ for _, {{.Name}}Item := range {{.Name}} {
+ {{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
+ }{{end}}{{end}}
+
+ sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.UnSubscribe()
+ for {
+ select {
+ case log := <-sub.EventMsgCh:
+ // New log arrived, parse the event and forward to the user
+ event := new({{$contract.Type}}{{.Normalized.Name}})
+ if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", *log); err != nil {
+ return err
+ }
+ event.Raw = *log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+ }
+
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Get{{.Normalized.Name}}PastEvent(txHash string, ContractLogs string) ([]*{{$contract.Type}}{{.Normalized.Name}}, error) {
+ var logRaws [] *data.Log
+ var err error
+ if ContractLogs != "" {
+ logRaws, err = _{{$contract.Type}}.contract.GetPastEventByCtrLog(ContractLogs)
+ } else if txHash != "" {
+ logRaws, err = _{{$contract.Type}}.contract.GetPastEventByTxHash(txHash)
+ } else {
+ return nil, errors.New("both txHash or ContractLogs is not provided for param")
+ }
+
+ if err != nil {
+ return nil, err
+ }
+ var events []*{{$contract.Type}}{{.Normalized.Name}}
+ for _, logRaw := range logRaws {
+ event, err := _{{$contract.Type}}.Parse{{.Normalized.Name}}(*logRaw)
+ if err != nil && err.Error() != "event signature mismatch" {
+ return nil, err
+ }
+ if event != nil {
+ events = append(events, event)
+ }
+ }
+ return events, nil
+ }
+
+ // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log data.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
+ event := new({{$contract.Type}}{{.Normalized.Name}})
+ if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+ }
+
+ {{end}}
+{{end}}
+`
+
+// tmplSourceJava is the Java source template that the generated Java contract binding
+// is based on.
+const tmplSourceJava = `
+// This file is an automatically generated Java binding. Do not modify as any
+// change will likely be lost upon the next re-generation!
+
+package {{.Package}};
+
+import org.ChainSQL.geth.*;
+import java.util.*;
+
+{{$structs := .Structs}}
+{{range $contract := .Contracts}}
+{{if not .Library}}public {{end}}class {{.Type}} {
+ // ABI is the input ABI used to generate the binding from.
+ public final static String ABI = "{{.InputABI}}";
+ {{if $contract.FuncSigs}}
+ // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
+ public final static Map {{.Type}}FuncSigs;
+ static {
+ Hashtable temp = new Hashtable();
+ {{range $strsig, $binsig := .FuncSigs}}temp.put("{{$binsig}}", "{{$strsig}}");
+ {{end}}
+ {{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
+ }
+ {{end}}
+ {{if .InputBin}}
+ // BYTECODE is the compiled bytecode used for deploying new contracts.
+ public final static String BYTECODE = "0x{{.InputBin}}";
+
+ // deploy deploys a new ChainSQL contract, binding an instance of {{.Type}} to it.
+ public static {{.Type}} deploy(TransactOpts auth, ChainSQLClient client{{range .Constructor.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
+ String bytecode = BYTECODE;
+ {{if .Libraries}}
+
+ // "link" contract to dependent libraries by deploying them first.
+ {{range $pattern, $name := .Libraries}}
+ {{capitalise $name}} {{decapitalise $name}}Inst = {{capitalise $name}}.deploy(auth, client);
+ bytecode = bytecode.replace("__${{$pattern}}$__", {{decapitalise $name}}Inst.Address.getHex().substring(2));
+ {{end}}
+ {{end}}
+ {{range $index, $element := .Constructor.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+ return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(bytecode), client, args));
+ }
+
+ // Internal constructor used by contract deployment.
+ private {{.Type}}(BoundContract deployment) {
+ this.Address = deployment.getAddress();
+ this.Deployer = deployment.getDeployer();
+ this.Contract = deployment;
+ }
+ {{end}}
+
+ // ChainSQL address where this contract is located at.
+ public final Address Address;
+
+ // ChainSQL transaction in which this contract was deployed (if known!).
+ public final Transaction Deployer;
+
+ // Contract instance bound to a blockchain address.
+ private final BoundContract Contract;
+
+ // Creates a new instance of {{.Type}}, bound to a specific deployed contract.
+ public {{.Type}}(Address address, ChainSQLClient client) throws Exception {
+ this(Geth.bindContract(address, ABI, client));
+ }
+
+ {{range .Calls}}
+ {{if gt (len .Normalized.Outputs) 1}}
+ // {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}.
+ public class {{capitalise .Normalized.Name}}Results {
+ {{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type $structs}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}};
+ {{end}}
+ }
+ {{end}}
+
+ // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else if eq (len .Normalized.Outputs) 0}}void{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
+ {{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+
+ Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}});
+ {{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type $structs) .Type}}(); results.set({{$index}}, result{{$index}});
+ {{end}}
+
+ if (opts == null) {
+ opts = Geth.newCallOpts();
+ }
+ this.Contract.call(opts, results, "{{.Original.Name}}", args);
+ {{if gt (len .Normalized.Outputs) 1}}
+ {{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results();
+ {{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type $structs) .Type}}();
+ {{end}}
+ return result;
+ {{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type $structs) .Type}}();{{end}}
+ {{end}}
+ }
+ {{end}}
+
+ {{range .Transacts}}
+ // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
+ //
+ // Solidity: {{.Original.String}}
+ public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
+ Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
+ {{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
+ {{end}}
+ return this.Contract.transact(opts, "{{.Original.Name}}" , args);
+ }
+ {{end}}
+
+ {{if .Fallback}}
+ // Fallback is a paid mutator transaction binding the contract fallback function.
+ //
+ // Solidity: {{.Fallback.Original.String}}
+ public Transaction Fallback(TransactOpts opts, byte[] calldata) throws Exception {
+ return this.Contract.rawTransact(opts, calldata);
+ }
+ {{end}}
+
+ {{if .Receive}}
+ // Receive is a paid mutator transaction binding the contract receive function.
+ //
+ // Solidity: {{.Receive.Original.String}}
+ public Transaction Receive(TransactOpts opts) throws Exception {
+ return this.Contract.rawTransact(opts, null);
+ }
+ {{end}}
+}
+{{end}}
+`
diff --git a/abigen/abi/error.go b/abigen/abi/error.go
new file mode 100644
index 0000000..1a204c2
--- /dev/null
+++ b/abigen/abi/error.go
@@ -0,0 +1,93 @@
+// Copyright 2016 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 abi
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/crypto"
+)
+
+type Error struct {
+ Name string
+ Inputs Arguments
+ str string
+
+ // Sig contains the string signature according to the ABI spec.
+ // e.g. error foo(uint32 a, int b) = "foo(uint32,int256)"
+ // Please note that "int" is substitute for its canonical representation "int256"
+ Sig string
+
+ // ID returns the canonical representation of the error's signature used by the
+ // abi definition to identify event names and types.
+ ID common.Hash
+}
+
+func NewError(name string, inputs Arguments) Error {
+ // sanitize inputs to remove inputs without names
+ // and precompute string and sig representation.
+ names := make([]string, len(inputs))
+ types := make([]string, len(inputs))
+ for i, input := range inputs {
+ if input.Name == "" {
+ inputs[i] = Argument{
+ Name: fmt.Sprintf("arg%d", i),
+ Indexed: input.Indexed,
+ Type: input.Type,
+ }
+ } else {
+ inputs[i] = input
+ }
+ // string representation
+ names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
+ if input.Indexed {
+ names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
+ }
+ // sig representation
+ types[i] = input.Type.String()
+ }
+
+ str := fmt.Sprintf("error %v(%v)", name, strings.Join(names, ", "))
+ sig := fmt.Sprintf("%v(%v)", name, strings.Join(types, ","))
+ id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
+
+ return Error{
+ Name: name,
+ Inputs: inputs,
+ str: str,
+ Sig: sig,
+ ID: id,
+ }
+}
+
+func (e *Error) String() string {
+ return e.str
+}
+
+func (e *Error) Unpack(data []byte) (interface{}, error) {
+ if len(data) < 4 {
+ return "", errors.New("invalid data for unpacking")
+ }
+ if !bytes.Equal(data[:4], e.ID[:4]) {
+ return "", errors.New("invalid data for unpacking")
+ }
+ return e.Inputs.Unpack(data[4:])
+}
diff --git a/abigen/abi/error_handling.go b/abigen/abi/error_handling.go
new file mode 100644
index 0000000..f0f71b6
--- /dev/null
+++ b/abigen/abi/error_handling.go
@@ -0,0 +1,82 @@
+// Copyright 2016 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 abi
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+var (
+ errBadBool = errors.New("abi: improperly encoded boolean value")
+)
+
+// formatSliceString formats the reflection kind with the given slice size
+// and returns a formatted string representation.
+func formatSliceString(kind reflect.Kind, sliceSize int) string {
+ if sliceSize == -1 {
+ return fmt.Sprintf("[]%v", kind)
+ }
+ return fmt.Sprintf("[%d]%v", sliceSize, kind)
+}
+
+// sliceTypeCheck checks that the given slice can by assigned to the reflection
+// type in t.
+func sliceTypeCheck(t Type, val reflect.Value) error {
+ if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
+ return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
+ }
+
+ if t.T == ArrayTy && val.Len() != t.Size {
+ return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
+ }
+
+ if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
+ if val.Len() > 0 {
+ return sliceTypeCheck(*t.Elem, val.Index(0))
+ }
+ }
+
+ if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
+ return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
+ }
+ return nil
+}
+
+// typeCheck checks that the given reflection value can be assigned to the reflection
+// type in t.
+func typeCheck(t Type, value reflect.Value) error {
+ if t.T == SliceTy || t.T == ArrayTy {
+ return sliceTypeCheck(t, value)
+ }
+
+ // Check base type validity. Element types will be checked later on.
+ if t.GetType().Kind() != value.Kind() {
+ return typeErr(t.GetType().Kind(), value.Kind())
+ } else if t.T == FixedBytesTy && t.Size != value.Len() {
+ return typeErr(t.GetType(), value.Type())
+ } else {
+ return nil
+ }
+
+}
+
+// typeErr returns a formatted type casting error.
+func typeErr(expected, got interface{}) error {
+ return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
+}
diff --git a/abigen/abi/event.go b/abigen/abi/event.go
new file mode 100644
index 0000000..20aaf82
--- /dev/null
+++ b/abigen/abi/event.go
@@ -0,0 +1,103 @@
+// Copyright 2016 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 abi
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/crypto"
+)
+
+// Event is an event potentially triggered by the EVM's LOG mechanism. The Event
+// holds type information (inputs) about the yielded output. Anonymous events
+// don't get the signature canonical representation as the first LOG topic.
+type Event struct {
+ // Name is the event name used for internal representation. It's derived from
+ // the raw name and a suffix will be added in the case of event overloading.
+ //
+ // e.g.
+ // These are two events that have the same name:
+ // * foo(int,int)
+ // * foo(uint,uint)
+ // The event name of the first one will be resolved as foo while the second one
+ // will be resolved as foo0.
+ Name string
+
+ // RawName is the raw event name parsed from ABI.
+ RawName string
+ Anonymous bool
+ Inputs Arguments
+ str string
+
+ // Sig contains the string signature according to the ABI spec.
+ // e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
+ // Please note that "int" is substitute for its canonical representation "int256"
+ Sig string
+
+ // ID returns the canonical representation of the event's signature used by the
+ // abi definition to identify event names and types.
+ ID common.Hash
+}
+
+// NewEvent creates a new Event.
+// It sanitizes the input arguments to remove unnamed arguments.
+// It also precomputes the id, signature and string representation
+// of the event.
+func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
+ // sanitize inputs to remove inputs without names
+ // and precompute string and sig representation.
+ names := make([]string, len(inputs))
+ types := make([]string, len(inputs))
+ for i, input := range inputs {
+ if input.Name == "" {
+ inputs[i] = Argument{
+ Name: fmt.Sprintf("arg%d", i),
+ Indexed: input.Indexed,
+ Type: input.Type,
+ }
+ } else {
+ inputs[i] = input
+ }
+ // string representation
+ names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
+ if input.Indexed {
+ names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
+ }
+ // sig representation
+ types[i] = input.Type.String()
+ }
+
+ str := fmt.Sprintf("event %v(%v)", rawName, strings.Join(names, ", "))
+ sig := fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
+ id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
+
+ return Event{
+ Name: name,
+ RawName: rawName,
+ Anonymous: anonymous,
+ Inputs: inputs,
+ str: str,
+ Sig: sig,
+ ID: id,
+ }
+}
+
+func (e Event) String() string {
+ return e.str
+}
diff --git a/abigen/abi/method.go b/abigen/abi/method.go
new file mode 100644
index 0000000..8969efb
--- /dev/null
+++ b/abigen/abi/method.go
@@ -0,0 +1,167 @@
+// Copyright 2015 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 abi
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/crypto"
+)
+
+// FunctionType represents different types of functions a contract might have.
+type FunctionType int
+
+const (
+ // Constructor represents the constructor of the contract.
+ // The constructor function is called while deploying a contract.
+ Constructor FunctionType = iota
+ // Fallback represents the fallback function.
+ // This function is executed if no other function matches the given function
+ // signature and no receive function is specified.
+ Fallback
+ // Receive represents the receive function.
+ // This function is executed on plain Ether transfers.
+ Receive
+ // Function represents a normal function.
+ Function
+)
+
+// Method represents a callable given a `Name` and whether the method is a constant.
+// If the method is `Const` no transaction needs to be created for this
+// particular Method call. It can easily be simulated using a local VM.
+// For example a `Balance()` method only needs to retrieve something
+// from the storage and therefore requires no Tx to be sent to the
+// network. A method such as `Transact` does require a Tx and thus will
+// be flagged `false`.
+// Input specifies the required input parameters for this gives method.
+type Method struct {
+ // Name is the method name used for internal representation. It's derived from
+ // the raw name and a suffix will be added in the case of a function overload.
+ //
+ // e.g.
+ // These are two functions that have the same name:
+ // * foo(int,int)
+ // * foo(uint,uint)
+ // The method name of the first one will be resolved as foo while the second one
+ // will be resolved as foo0.
+ Name string
+ RawName string // RawName is the raw method name parsed from ABI
+
+ // Type indicates whether the method is a
+ // special fallback introduced in solidity v0.6.0
+ Type FunctionType
+
+ // StateMutability indicates the mutability state of method,
+ // the default value is nonpayable. It can be empty if the abi
+ // is generated by legacy compiler.
+ StateMutability string
+
+ // Legacy indicators generated by compiler before v0.6.0
+ Constant bool
+ Payable bool
+
+ Inputs Arguments
+ Outputs Arguments
+ str string
+ // Sig returns the methods string signature according to the ABI spec.
+ // e.g. function foo(uint32 a, int b) = "foo(uint32,int256)"
+ // Please note that "int" is substitute for its canonical representation "int256"
+ Sig string
+ // ID returns the canonical representation of the method's signature used by the
+ // abi definition to identify method names and types.
+ ID []byte
+}
+
+// NewMethod creates a new Method.
+// A method should always be created using NewMethod.
+// It also precomputes the sig representation and the string representation
+// of the method.
+func NewMethod(name string, rawName string, funType FunctionType, mutability string, isConst, isPayable bool, inputs Arguments, outputs Arguments) Method {
+ var (
+ types = make([]string, len(inputs))
+ inputNames = make([]string, len(inputs))
+ outputNames = make([]string, len(outputs))
+ )
+ for i, input := range inputs {
+ inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
+ types[i] = input.Type.String()
+ }
+ for i, output := range outputs {
+ outputNames[i] = output.Type.String()
+ if len(output.Name) > 0 {
+ outputNames[i] += fmt.Sprintf(" %v", output.Name)
+ }
+ }
+ // calculate the signature and method id. Note only function
+ // has meaningful signature and id.
+ var (
+ sig string
+ id []byte
+ )
+ if funType == Function {
+ sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
+ id = crypto.Keccak256([]byte(sig))[:4]
+ }
+ // Extract meaningful state mutability of solidity method.
+ // If it's default value, never print it.
+ state := mutability
+ if state == "nonpayable" {
+ state = ""
+ }
+ if state != "" {
+ state = state + " "
+ }
+ identity := fmt.Sprintf("function %v", rawName)
+ if funType == Fallback {
+ identity = "fallback"
+ } else if funType == Receive {
+ identity = "receive"
+ } else if funType == Constructor {
+ identity = "constructor"
+ }
+ str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
+
+ return Method{
+ Name: name,
+ RawName: rawName,
+ Type: funType,
+ StateMutability: mutability,
+ Constant: isConst,
+ Payable: isPayable,
+ Inputs: inputs,
+ Outputs: outputs,
+ str: str,
+ Sig: sig,
+ ID: id,
+ }
+}
+
+func (method Method) String() string {
+ return method.str
+}
+
+// IsConstant returns the indicator whether the method is read-only.
+func (method Method) IsConstant() bool {
+ return method.StateMutability == "view" || method.StateMutability == "pure" || method.Constant
+}
+
+// IsPayable returns the indicator whether the method can process
+// plain ether transfers.
+func (method Method) IsPayable() bool {
+ return method.StateMutability == "payable" || method.Payable
+}
diff --git a/abigen/abi/pack.go b/abigen/abi/pack.go
new file mode 100644
index 0000000..c9fe511
--- /dev/null
+++ b/abigen/abi/pack.go
@@ -0,0 +1,85 @@
+// Copyright 2016 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 abi
+
+import (
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/common/math"
+)
+
+// packBytesSlice packs the given bytes as [L, V] as the canonical representation
+// bytes slice.
+func packBytesSlice(bytes []byte, l int) []byte {
+ len := packNum(reflect.ValueOf(l))
+ return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...)
+}
+
+// packElement packs the given reflect value according to the abi specification in
+// t.
+func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
+ switch t.T {
+ case IntTy, UintTy:
+ return packNum(reflectValue), nil
+ case StringTy:
+ return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()), nil
+ case AddressTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+
+ return common.LeftPadBytes(reflectValue.Bytes(), 32), nil
+ case BoolTy:
+ if reflectValue.Bool() {
+ return math.PaddedBigBytes(common.Big1, 32), nil
+ }
+ return math.PaddedBigBytes(common.Big0, 32), nil
+ case BytesTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+ if reflectValue.Type() != reflect.TypeOf([]byte{}) {
+ return []byte{}, errors.New("Bytes type is neither slice nor array")
+ }
+ return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
+ case FixedBytesTy, FunctionTy:
+ if reflectValue.Kind() == reflect.Array {
+ reflectValue = mustArrayToByteSlice(reflectValue)
+ }
+ return common.RightPadBytes(reflectValue.Bytes(), 32), nil
+ default:
+ return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T)
+ }
+}
+
+// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation.
+func packNum(value reflect.Value) []byte {
+ switch kind := value.Kind(); kind {
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return math.U256Bytes(new(big.Int).SetUint64(value.Uint()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return math.U256Bytes(big.NewInt(value.Int()))
+ case reflect.Ptr:
+ return math.U256Bytes(new(big.Int).Set(value.Interface().(*big.Int)))
+ default:
+ panic("abi: fatal error")
+ }
+}
diff --git a/abigen/abi/reflect.go b/abigen/abi/reflect.go
new file mode 100644
index 0000000..35e5556
--- /dev/null
+++ b/abigen/abi/reflect.go
@@ -0,0 +1,260 @@
+// Copyright 2016 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 abi
+
+import (
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+ "strings"
+)
+
+// ConvertType converts an interface of a runtime type into a interface of the
+// given type
+// e.g. turn
+// var fields []reflect.StructField
+// fields = append(fields, reflect.StructField{
+// Name: "X",
+// Type: reflect.TypeOf(new(big.Int)),
+// Tag: reflect.StructTag("json:\"" + "x" + "\""),
+// }
+// into
+// type TupleT struct { X *big.Int }
+func ConvertType(in interface{}, proto interface{}) interface{} {
+ protoType := reflect.TypeOf(proto)
+ if reflect.TypeOf(in).ConvertibleTo(protoType) {
+ return reflect.ValueOf(in).Convert(protoType).Interface()
+ }
+ // Use set as a last ditch effort
+ if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil {
+ panic(err)
+ }
+ return proto
+}
+
+// indirect recursively dereferences the value until it either gets the value
+// or finds a big.Int
+func indirect(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
+ return indirect(v.Elem())
+ }
+ return v
+}
+
+// reflectIntType returns the reflect using the given size and
+// unsignedness.
+func reflectIntType(unsigned bool, size int) reflect.Type {
+ if unsigned {
+ switch size {
+ case 8:
+ return reflect.TypeOf(uint8(0))
+ case 16:
+ return reflect.TypeOf(uint16(0))
+ case 32:
+ return reflect.TypeOf(uint32(0))
+ case 64:
+ return reflect.TypeOf(uint64(0))
+ }
+ }
+ switch size {
+ case 8:
+ return reflect.TypeOf(int8(0))
+ case 16:
+ return reflect.TypeOf(int16(0))
+ case 32:
+ return reflect.TypeOf(int32(0))
+ case 64:
+ return reflect.TypeOf(int64(0))
+ }
+ return reflect.TypeOf(&big.Int{})
+}
+
+// mustArrayToByteSlice creates a new byte slice with the exact same size as value
+// and copies the bytes in value to the new slice.
+func mustArrayToByteSlice(value reflect.Value) reflect.Value {
+ slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
+ reflect.Copy(slice, value)
+ return slice
+}
+
+// set attempts to assign src to dst by either setting, copying or otherwise.
+//
+// set is a bit more lenient when it comes to assignment and doesn't force an as
+// strict ruleset as bare `reflect` does.
+func set(dst, src reflect.Value) error {
+ dstType, srcType := dst.Type(), src.Type()
+ switch {
+ case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
+ return set(dst.Elem(), src)
+ case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
+ return set(dst.Elem(), src)
+ case srcType.AssignableTo(dstType) && dst.CanSet():
+ dst.Set(src)
+ case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice && dst.CanSet():
+ return setSlice(dst, src)
+ case dstType.Kind() == reflect.Array:
+ return setArray(dst, src)
+ case dstType.Kind() == reflect.Struct:
+ return setStruct(dst, src)
+ default:
+ return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
+ }
+ return nil
+}
+
+// setSlice attempts to assign src to dst when slices are not assignable by default
+// e.g. src: [][]byte -> dst: [][15]byte
+// setSlice ignores if we cannot copy all of src' elements.
+func setSlice(dst, src reflect.Value) error {
+ slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
+ for i := 0; i < src.Len(); i++ {
+ if err := set(slice.Index(i), src.Index(i)); err != nil {
+ return err
+ }
+ }
+ if dst.CanSet() {
+ dst.Set(slice)
+ return nil
+ }
+ return errors.New("Cannot set slice, destination not settable")
+}
+
+func setArray(dst, src reflect.Value) error {
+ if src.Kind() == reflect.Ptr {
+ return set(dst, indirect(src))
+ }
+ array := reflect.New(dst.Type()).Elem()
+ min := src.Len()
+ if src.Len() > dst.Len() {
+ min = dst.Len()
+ }
+ for i := 0; i < min; i++ {
+ if err := set(array.Index(i), src.Index(i)); err != nil {
+ return err
+ }
+ }
+ if dst.CanSet() {
+ dst.Set(array)
+ return nil
+ }
+ return errors.New("Cannot set array, destination not settable")
+}
+
+func setStruct(dst, src reflect.Value) error {
+ for i := 0; i < src.NumField(); i++ {
+ srcField := src.Field(i)
+ dstField := dst.Field(i)
+ if !dstField.IsValid() || !srcField.IsValid() {
+ return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
+ }
+ if err := set(dstField, srcField); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// mapArgNamesToStructFields maps a slice of argument names to struct fields.
+// first round: for each Exportable field that contains a `abi:""` tag
+// and this field name exists in the given argument name list, pair them together.
+// second round: for each argument name that has not been already linked,
+// find what variable is expected to be mapped into, if it exists and has not been
+// used, pair them.
+// Note this function assumes the given value is a struct value.
+func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
+ typ := value.Type()
+
+ abi2struct := make(map[string]string)
+ struct2abi := make(map[string]string)
+
+ // first round ~~~
+ for i := 0; i < typ.NumField(); i++ {
+ structFieldName := typ.Field(i).Name
+
+ // skip private struct fields.
+ if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
+ continue
+ }
+ // skip fields that have no abi:"" tag.
+ tagName, ok := typ.Field(i).Tag.Lookup("abi")
+ if !ok {
+ continue
+ }
+ // check if tag is empty.
+ if tagName == "" {
+ return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
+ }
+ // check which argument field matches with the abi tag.
+ found := false
+ for _, arg := range argNames {
+ if arg == tagName {
+ if abi2struct[arg] != "" {
+ return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
+ }
+ // pair them
+ abi2struct[arg] = structFieldName
+ struct2abi[structFieldName] = arg
+ found = true
+ }
+ }
+ // check if this tag has been mapped.
+ if !found {
+ return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
+ }
+ }
+
+ // second round ~~~
+ for _, argName := range argNames {
+
+ structFieldName := ToCamelCase(argName)
+
+ if structFieldName == "" {
+ return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
+ }
+
+ // this abi has already been paired, skip it... unless there exists another, yet unassigned
+ // struct field with the same field name. If so, raise an error:
+ // abi: [ { "name": "value" } ]
+ // struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
+ if abi2struct[argName] != "" {
+ if abi2struct[argName] != structFieldName &&
+ struct2abi[structFieldName] == "" &&
+ value.FieldByName(structFieldName).IsValid() {
+ return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
+ }
+ continue
+ }
+
+ // return an error if this struct field has already been paired.
+ if struct2abi[structFieldName] != "" {
+ return nil, fmt.Errorf("abi: multiple outputs mapping to the same struct field '%s'", structFieldName)
+ }
+
+ if value.FieldByName(structFieldName).IsValid() {
+ // pair them
+ abi2struct[argName] = structFieldName
+ struct2abi[structFieldName] = argName
+ } else {
+ // not paired, but annotate as used, to detect cases like
+ // abi : [ { "name": "value" }, { "name": "_value" } ]
+ // struct { Value *big.Int }
+ struct2abi[structFieldName] = argName
+ }
+ }
+ return abi2struct, nil
+}
diff --git a/abigen/abi/topics.go b/abigen/abi/topics.go
new file mode 100644
index 0000000..19feabb
--- /dev/null
+++ b/abigen/abi/topics.go
@@ -0,0 +1,173 @@
+// Copyright 2018 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 abi
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math/big"
+ "reflect"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/crypto"
+)
+
+// MakeTopics converts a filter query argument list into a filter topic set.
+func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
+ topics := make([][]common.Hash, len(query))
+ for i, filter := range query {
+ for _, rule := range filter {
+ var topic common.Hash
+
+ // Try to generate the topic based on simple types
+ switch rule := rule.(type) {
+ case common.Hash:
+ copy(topic[:], rule[:])
+ case common.Address:
+ copy(topic[common.HashLength-common.AddressLength:], rule[:])
+ case *big.Int:
+ blob := rule.Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case bool:
+ if rule {
+ topic[common.HashLength-1] = 1
+ }
+ case int8:
+ copy(topic[:], genIntType(int64(rule), 1))
+ case int16:
+ copy(topic[:], genIntType(int64(rule), 2))
+ case int32:
+ copy(topic[:], genIntType(int64(rule), 4))
+ case int64:
+ copy(topic[:], genIntType(rule, 8))
+ case uint8:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case uint16:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case uint32:
+ blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case uint64:
+ blob := new(big.Int).SetUint64(rule).Bytes()
+ copy(topic[common.HashLength-len(blob):], blob)
+ case string:
+ hash := crypto.Keccak256Hash([]byte(rule))
+ copy(topic[:], hash[:])
+ case []byte:
+ hash := crypto.Keccak256Hash(rule)
+ copy(topic[:], hash[:])
+
+ default:
+ // todo(rjl493456442) according solidity documentation, indexed event
+ // parameters that are not value types i.e. arrays and structs are not
+ // stored directly but instead a keccak256-hash of an encoding is stored.
+ //
+ // We only convert stringS and bytes to hash, still need to deal with
+ // array(both fixed-size and dynamic-size) and struct.
+
+ // Attempt to generate the topic from funky types
+ val := reflect.ValueOf(rule)
+ switch {
+ // static byte array
+ case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
+ reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
+ default:
+ return nil, fmt.Errorf("unsupported indexed type: %T", rule)
+ }
+ }
+ topics[i] = append(topics[i], topic)
+ }
+ }
+ return topics, nil
+}
+
+func genIntType(rule int64, size uint) []byte {
+ var topic [common.HashLength]byte
+ if rule < 0 {
+ // if a rule is negative, we need to put it into two's complement.
+ // extended to common.HashLength bytes.
+ topic = [common.HashLength]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
+ }
+ for i := uint(0); i < size; i++ {
+ topic[common.HashLength-i-1] = byte(rule >> (i * 8))
+ }
+ return topic[:]
+}
+
+// ParseTopics converts the indexed topic fields into actual log field values.
+func ParseTopics(out interface{}, fields Arguments, topics []common.Hash) error {
+ return parseTopicWithSetter(fields, topics,
+ func(arg Argument, reconstr interface{}) {
+ field := reflect.ValueOf(out).Elem().FieldByName(ToCamelCase(arg.Name))
+ field.Set(reflect.ValueOf(reconstr))
+ })
+}
+
+// ParseTopicsIntoMap converts the indexed topic field-value pairs into map key-value pairs.
+func ParseTopicsIntoMap(out map[string]interface{}, fields Arguments, topics []common.Hash) error {
+ return parseTopicWithSetter(fields, topics,
+ func(arg Argument, reconstr interface{}) {
+ out[arg.Name] = reconstr
+ })
+}
+
+// parseTopicWithSetter converts the indexed topic field-value pairs and stores them using the
+// provided set function.
+//
+// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
+// hashes as the topic value!
+func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Argument, interface{})) error {
+ // Sanity check that the fields and topics match up
+ if len(fields) != len(topics) {
+ return errors.New("topic/field count mismatch")
+ }
+ // Iterate over all the fields and reconstruct them from topics
+ for i, arg := range fields {
+ if !arg.Indexed {
+ return errors.New("non-indexed field in topic reconstruction")
+ }
+ var reconstr interface{}
+ switch arg.Type.T {
+ case TupleTy:
+ return errors.New("tuple type in topic reconstruction")
+ case StringTy, BytesTy, SliceTy, ArrayTy:
+ // Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
+ // whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash
+ reconstr = topics[i]
+ case FunctionTy:
+ if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 {
+ return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
+ }
+ var tmp [24]byte
+ copy(tmp[:], topics[i][8:32])
+ reconstr = tmp
+ default:
+ var err error
+ reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
+ if err != nil {
+ return err
+ }
+ }
+ // Use the setter function to store the value
+ setter(arg, reconstr)
+ }
+
+ return nil
+}
diff --git a/abigen/abi/type.go b/abigen/abi/type.go
new file mode 100644
index 0000000..e3b99bb
--- /dev/null
+++ b/abigen/abi/type.go
@@ -0,0 +1,423 @@
+// Copyright 2015 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 abi
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+)
+
+// Type enumerator
+const (
+ IntTy byte = iota
+ UintTy
+ BoolTy
+ StringTy
+ SliceTy
+ ArrayTy
+ TupleTy
+ AddressTy
+ FixedBytesTy
+ BytesTy
+ HashTy
+ FixedPointTy
+ FunctionTy
+)
+
+// Type is the reflection of the supported argument type.
+type Type struct {
+ Elem *Type
+ Size int
+ T byte // Our own type checking
+
+ stringKind string // holds the unparsed string for deriving signatures
+
+ // Tuple relative fields
+ TupleRawName string // Raw struct name defined in source code, may be empty.
+ TupleElems []*Type // Type information of all tuple fields
+ TupleRawNames []string // Raw field name of all tuple fields
+ TupleType reflect.Type // Underlying struct of the tuple
+}
+
+var (
+ // typeRegex parses the abi sub types
+ typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
+)
+
+// NewType creates a new reflection type of abi type given in t.
+func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) {
+ // check that array brackets are equal if they exist
+ if strings.Count(t, "[") != strings.Count(t, "]") {
+ return Type{}, fmt.Errorf("invalid arg type in abi")
+ }
+ typ.stringKind = t
+
+ // if there are brackets, get ready to go into slice/array mode and
+ // recursively create the type
+ if strings.Count(t, "[") != 0 {
+ // Note internalType can be empty here.
+ subInternal := internalType
+ if i := strings.LastIndex(internalType, "["); i != -1 {
+ subInternal = subInternal[:i]
+ }
+ // recursively embed the type
+ i := strings.LastIndex(t, "[")
+ embeddedType, err := NewType(t[:i], subInternal, components)
+ if err != nil {
+ return Type{}, err
+ }
+ // grab the last cell and create a type from there
+ sliced := t[i:]
+ // grab the slice size with regexp
+ re := regexp.MustCompile("[0-9]+")
+ intz := re.FindAllString(sliced, -1)
+
+ if len(intz) == 0 {
+ // is a slice
+ typ.T = SliceTy
+ typ.Elem = &embeddedType
+ typ.stringKind = embeddedType.stringKind + sliced
+ } else if len(intz) == 1 {
+ // is an array
+ typ.T = ArrayTy
+ typ.Elem = &embeddedType
+ typ.Size, err = strconv.Atoi(intz[0])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
+ typ.stringKind = embeddedType.stringKind + sliced
+ } else {
+ return Type{}, fmt.Errorf("invalid formatting of array type")
+ }
+ return typ, err
+ }
+ // parse the type and size of the abi-type.
+ matches := typeRegex.FindAllStringSubmatch(t, -1)
+ if len(matches) == 0 {
+ return Type{}, fmt.Errorf("invalid type '%v'", t)
+ }
+ parsedType := matches[0]
+
+ // varSize is the size of the variable
+ var varSize int
+ if len(parsedType[3]) > 0 {
+ var err error
+ varSize, err = strconv.Atoi(parsedType[2])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
+ } else {
+ if parsedType[0] == "uint" || parsedType[0] == "int" {
+ // this should fail because it means that there's something wrong with
+ // the abi type (the compiler should always format it to the size...always)
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+ }
+ }
+ // varType is the parsed abi type
+ switch varType := parsedType[1]; varType {
+ case "int":
+ typ.Size = varSize
+ typ.T = IntTy
+ case "uint":
+ typ.Size = varSize
+ typ.T = UintTy
+ case "bool":
+ typ.T = BoolTy
+ case "address":
+ typ.Size = 20
+ typ.T = AddressTy
+ case "string":
+ typ.T = StringTy
+ case "bytes":
+ if varSize == 0 {
+ typ.T = BytesTy
+ } else {
+ typ.T = FixedBytesTy
+ typ.Size = varSize
+ }
+ case "tuple":
+ var (
+ fields []reflect.StructField
+ elems []*Type
+ names []string
+ expression string // canonical parameter expression
+ used = make(map[string]bool)
+ )
+ expression += "("
+ for idx, c := range components {
+ cType, err := NewType(c.Type, c.InternalType, c.Components)
+ if err != nil {
+ return Type{}, err
+ }
+ name := ToCamelCase(c.Name)
+ if name == "" {
+ return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
+ }
+ fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] })
+ if err != nil {
+ return Type{}, err
+ }
+ used[fieldName] = true
+ if !isValidFieldName(fieldName) {
+ return Type{}, fmt.Errorf("field %d has invalid name", idx)
+ }
+ fields = append(fields, reflect.StructField{
+ Name: fieldName, // reflect.StructOf will panic for any exported field.
+ Type: cType.GetType(),
+ Tag: reflect.StructTag("json:\"" + c.Name + "\""),
+ })
+ elems = append(elems, &cType)
+ names = append(names, c.Name)
+ expression += cType.stringKind
+ if idx != len(components)-1 {
+ expression += ","
+ }
+ }
+ expression += ")"
+
+ typ.TupleType = reflect.StructOf(fields)
+ typ.TupleElems = elems
+ typ.TupleRawNames = names
+ typ.T = TupleTy
+ typ.stringKind = expression
+
+ const structPrefix = "struct "
+ // After solidity 0.5.10, a new field of abi "internalType"
+ // is introduced. From that we can obtain the struct name
+ // user defined in the source code.
+ if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
+ // Foo.Bar type definition is not allowed in golang,
+ // convert the format to FooBar
+ typ.TupleRawName = strings.ReplaceAll(internalType[len(structPrefix):], ".", "")
+ }
+
+ case "function":
+ typ.T = FunctionTy
+ typ.Size = 24
+ default:
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+ }
+
+ return
+}
+
+// GetType returns the reflection type of the ABI type.
+func (t Type) GetType() reflect.Type {
+ switch t.T {
+ case IntTy:
+ return reflectIntType(false, t.Size)
+ case UintTy:
+ return reflectIntType(true, t.Size)
+ case BoolTy:
+ return reflect.TypeOf(false)
+ case StringTy:
+ return reflect.TypeOf("")
+ case SliceTy:
+ return reflect.SliceOf(t.Elem.GetType())
+ case ArrayTy:
+ return reflect.ArrayOf(t.Size, t.Elem.GetType())
+ case TupleTy:
+ return t.TupleType
+ case AddressTy:
+ return reflect.TypeOf(common.Address{})
+ case FixedBytesTy:
+ return reflect.ArrayOf(t.Size, reflect.TypeOf(byte(0)))
+ case BytesTy:
+ return reflect.SliceOf(reflect.TypeOf(byte(0)))
+ case HashTy:
+ // hashtype currently not used
+ return reflect.ArrayOf(32, reflect.TypeOf(byte(0)))
+ case FixedPointTy:
+ // fixedpoint type currently not used
+ return reflect.ArrayOf(32, reflect.TypeOf(byte(0)))
+ case FunctionTy:
+ return reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
+ default:
+ panic("Invalid type")
+ }
+}
+
+// String implements Stringer.
+func (t Type) String() (out string) {
+ return t.stringKind
+}
+
+func (t Type) pack(v reflect.Value) ([]byte, error) {
+ // dereference pointer first if it's a pointer
+ v = indirect(v)
+ if err := typeCheck(t, v); err != nil {
+ return nil, err
+ }
+
+ switch t.T {
+ case SliceTy, ArrayTy:
+ var ret []byte
+
+ if t.requiresLengthPrefix() {
+ // append length
+ ret = append(ret, packNum(reflect.ValueOf(v.Len()))...)
+ }
+
+ // calculate offset if any
+ offset := 0
+ offsetReq := isDynamicType(*t.Elem)
+ if offsetReq {
+ offset = getTypeSize(*t.Elem) * v.Len()
+ }
+ var tail []byte
+ for i := 0; i < v.Len(); i++ {
+ val, err := t.Elem.pack(v.Index(i))
+ if err != nil {
+ return nil, err
+ }
+ if !offsetReq {
+ ret = append(ret, val...)
+ continue
+ }
+ ret = append(ret, packNum(reflect.ValueOf(offset))...)
+ offset += len(val)
+ tail = append(tail, val...)
+ }
+ return append(ret, tail...), nil
+ case TupleTy:
+ // (T1,...,Tk) for k >= 0 and any types T1, …, Tk
+ // enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))
+ // where X = (X(1), ..., X(k)) and head and tail are defined for Ti being a static
+ // type as
+ // head(X(i)) = enc(X(i)) and tail(X(i)) = "" (the empty string)
+ // and as
+ // head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1))))
+ // tail(X(i)) = enc(X(i))
+ // otherwise, i.e. if Ti is a dynamic type.
+ fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, v)
+ if err != nil {
+ return nil, err
+ }
+ // Calculate prefix occupied size.
+ offset := 0
+ for _, elem := range t.TupleElems {
+ offset += getTypeSize(*elem)
+ }
+ var ret, tail []byte
+ for i, elem := range t.TupleElems {
+ field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
+ if !field.IsValid() {
+ return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
+ }
+ val, err := elem.pack(field)
+ if err != nil {
+ return nil, err
+ }
+ if isDynamicType(*elem) {
+ ret = append(ret, packNum(reflect.ValueOf(offset))...)
+ tail = append(tail, val...)
+ offset += len(val)
+ } else {
+ ret = append(ret, val...)
+ }
+ }
+ return append(ret, tail...), nil
+
+ default:
+ return packElement(t, v)
+ }
+}
+
+// requireLengthPrefix returns whether the type requires any sort of length
+// prefixing.
+func (t Type) requiresLengthPrefix() bool {
+ return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
+}
+
+// isDynamicType returns true if the type is dynamic.
+// The following types are called “dynamic”:
+// * bytes
+// * string
+// * T[] for any T
+// * T[k] for any dynamic T and any k >= 0
+// * (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k
+func isDynamicType(t Type) bool {
+ if t.T == TupleTy {
+ for _, elem := range t.TupleElems {
+ if isDynamicType(*elem) {
+ return true
+ }
+ }
+ return false
+ }
+ return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
+}
+
+// getTypeSize returns the size that this type needs to occupy.
+// We distinguish static and dynamic types. Static types are encoded in-place
+// and dynamic types are encoded at a separately allocated location after the
+// current block.
+// So for a static variable, the size returned represents the size that the
+// variable actually occupies.
+// For a dynamic variable, the returned size is fixed 32 bytes, which is used
+// to store the location reference for actual value storage.
+func getTypeSize(t Type) int {
+ if t.T == ArrayTy && !isDynamicType(*t.Elem) {
+ // Recursively calculate type size if it is a nested array
+ if t.Elem.T == ArrayTy || t.Elem.T == TupleTy {
+ return t.Size * getTypeSize(*t.Elem)
+ }
+ return t.Size * 32
+ } else if t.T == TupleTy && !isDynamicType(t) {
+ total := 0
+ for _, elem := range t.TupleElems {
+ total += getTypeSize(*elem)
+ }
+ return total
+ }
+ return 32
+}
+
+// isLetter reports whether a given 'rune' is classified as a Letter.
+// This method is copied from reflect/type.go
+func isLetter(ch rune) bool {
+ return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
+}
+
+// isValidFieldName checks if a string is a valid (struct) field name or not.
+//
+// According to the language spec, a field name should be an identifier.
+//
+// identifier = letter { letter | unicode_digit } .
+// letter = unicode_letter | "_" .
+// This method is copied from reflect/type.go
+func isValidFieldName(fieldName string) bool {
+ for i, c := range fieldName {
+ if i == 0 && !isLetter(c) {
+ return false
+ }
+
+ if !(isLetter(c) || unicode.IsDigit(c)) {
+ return false
+ }
+ }
+
+ return len(fieldName) > 0
+}
diff --git a/abigen/abi/unpack.go b/abigen/abi/unpack.go
new file mode 100644
index 0000000..9153e25
--- /dev/null
+++ b/abigen/abi/unpack.go
@@ -0,0 +1,298 @@
+// Copyright 2017 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 abi
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math/big"
+ "reflect"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+)
+
+var (
+ // MaxUint256 is the maximum value that can be represented by a uint256.
+ MaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1)
+ // MaxInt256 is the maximum value that can be represented by a int256.
+ MaxInt256 = new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 255), common.Big1)
+)
+
+// ReadInteger reads the integer based on its kind and returns the appropriate value.
+func ReadInteger(typ Type, b []byte) interface{} {
+ if typ.T == UintTy {
+ switch typ.Size {
+ case 8:
+ return b[len(b)-1]
+ case 16:
+ return binary.BigEndian.Uint16(b[len(b)-2:])
+ case 32:
+ return binary.BigEndian.Uint32(b[len(b)-4:])
+ case 64:
+ return binary.BigEndian.Uint64(b[len(b)-8:])
+ default:
+ // the only case left for unsigned integer is uint256.
+ return new(big.Int).SetBytes(b)
+ }
+ }
+ switch typ.Size {
+ case 8:
+ return int8(b[len(b)-1])
+ case 16:
+ return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
+ case 32:
+ return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
+ case 64:
+ return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
+ default:
+ // the only case left for integer is int256
+ // big.SetBytes can't tell if a number is negative or positive in itself.
+ // On EVM, if the returned number > max int256, it is negative.
+ // A number is > max int256 if the bit at position 255 is set.
+ ret := new(big.Int).SetBytes(b)
+ if ret.Bit(255) == 1 {
+ ret.Add(MaxUint256, new(big.Int).Neg(ret))
+ ret.Add(ret, common.Big1)
+ ret.Neg(ret)
+ }
+ return ret
+ }
+}
+
+// readBool reads a bool.
+func readBool(word []byte) (bool, error) {
+ for _, b := range word[:31] {
+ if b != 0 {
+ return false, errBadBool
+ }
+ }
+ switch word[31] {
+ case 0:
+ return false, nil
+ case 1:
+ return true, nil
+ default:
+ return false, errBadBool
+ }
+}
+
+// A function type is simply the address with the function selection signature at the end.
+//
+// readFunctionType enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
+func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
+ if t.T != FunctionTy {
+ return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
+ }
+ if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
+ err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
+ } else {
+ copy(funcTy[:], word[0:24])
+ }
+ return
+}
+
+// ReadFixedBytes uses reflection to create a fixed array to be read from.
+func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
+ if t.T != FixedBytesTy {
+ return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
+ }
+ // convert
+ array := reflect.New(t.GetType()).Elem()
+
+ reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
+ return array.Interface(), nil
+
+}
+
+// forEachUnpack iteratively unpack elements.
+func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
+ if size < 0 {
+ return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
+ }
+ if start+32*size > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
+ }
+
+ // this value will become our slice or our array, depending on the type
+ var refSlice reflect.Value
+
+ if t.T == SliceTy {
+ // declare our slice
+ refSlice = reflect.MakeSlice(t.GetType(), size, size)
+ } else if t.T == ArrayTy {
+ // declare our array
+ refSlice = reflect.New(t.GetType()).Elem()
+ } else {
+ return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
+ }
+
+ // Arrays have packed elements, resulting in longer unpack steps.
+ // Slices have just 32 bytes per element (pointing to the contents).
+ elemSize := getTypeSize(*t.Elem)
+
+ for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
+ inter, err := toGoType(i, *t.Elem, output)
+ if err != nil {
+ return nil, err
+ }
+
+ // append the item to our reflect slice
+ refSlice.Index(j).Set(reflect.ValueOf(inter))
+ }
+
+ // return the interface
+ return refSlice.Interface(), nil
+}
+
+func forTupleUnpack(t Type, output []byte) (interface{}, error) {
+ retval := reflect.New(t.GetType()).Elem()
+ virtualArgs := 0
+ for index, elem := range t.TupleElems {
+ marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
+ if elem.T == ArrayTy && !isDynamicType(*elem) {
+ // If we have a static array, like [3]uint256, these are coded as
+ // just like uint256,uint256,uint256.
+ // This means that we need to add two 'virtual' arguments when
+ // we count the index from now on.
+ //
+ // Array values nested multiple levels deep are also encoded inline:
+ // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
+ //
+ // Calculate the full array size to get the correct offset for the next argument.
+ // Decrement it by 1, as the normal index increment is still applied.
+ virtualArgs += getTypeSize(*elem)/32 - 1
+ } else if elem.T == TupleTy && !isDynamicType(*elem) {
+ // If we have a static tuple, like (uint256, bool, uint256), these are
+ // coded as just like uint256,bool,uint256
+ virtualArgs += getTypeSize(*elem)/32 - 1
+ }
+ if err != nil {
+ return nil, err
+ }
+ retval.Field(index).Set(reflect.ValueOf(marshalledValue))
+ }
+ return retval.Interface(), nil
+}
+
+// toGoType parses the output bytes and recursively assigns the value of these bytes
+// into a go type with accordance with the ABI spec.
+func toGoType(index int, t Type, output []byte) (interface{}, error) {
+ if index+32 > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
+ }
+
+ var (
+ returnOutput []byte
+ begin, length int
+ err error
+ )
+
+ // if we require a length prefix, find the beginning word and size returned.
+ if t.requiresLengthPrefix() {
+ begin, length, err = lengthPrefixPointsTo(index, output)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ returnOutput = output[index : index+32]
+ }
+
+ switch t.T {
+ case TupleTy:
+ if isDynamicType(t) {
+ begin, err := tuplePointsTo(index, output)
+ if err != nil {
+ return nil, err
+ }
+ return forTupleUnpack(t, output[begin:])
+ }
+ return forTupleUnpack(t, output[index:])
+ case SliceTy:
+ return forEachUnpack(t, output[begin:], 0, length)
+ case ArrayTy:
+ if isDynamicType(*t.Elem) {
+ offset := binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:])
+ if offset > uint64(len(output)) {
+ return nil, fmt.Errorf("abi: toGoType offset greater than output length: offset: %d, len(output): %d", offset, len(output))
+ }
+ return forEachUnpack(t, output[offset:], 0, t.Size)
+ }
+ return forEachUnpack(t, output[index:], 0, t.Size)
+ case StringTy: // variable arrays are written at the end of the return bytes
+ return string(output[begin : begin+length]), nil
+ case IntTy, UintTy:
+ return ReadInteger(t, returnOutput), nil
+ case BoolTy:
+ return readBool(returnOutput)
+ case AddressTy:
+ return common.BytesToAddress(returnOutput), nil
+ case HashTy:
+ return common.BytesToHash(returnOutput), nil
+ case BytesTy:
+ return output[begin : begin+length], nil
+ case FixedBytesTy:
+ return ReadFixedBytes(t, returnOutput)
+ case FunctionTy:
+ return readFunctionType(t, returnOutput)
+ default:
+ return nil, fmt.Errorf("abi: unknown type %v", t.T)
+ }
+}
+
+// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
+func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
+ bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32])
+ bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
+ outputLength := big.NewInt(int64(len(output)))
+
+ if bigOffsetEnd.Cmp(outputLength) > 0 {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", bigOffsetEnd, outputLength)
+ }
+
+ if bigOffsetEnd.BitLen() > 63 {
+ return 0, 0, fmt.Errorf("abi offset larger than int64: %v", bigOffsetEnd)
+ }
+
+ offsetEnd := int(bigOffsetEnd.Uint64())
+ lengthBig := new(big.Int).SetBytes(output[offsetEnd-32 : offsetEnd])
+
+ totalSize := new(big.Int).Add(bigOffsetEnd, lengthBig)
+ if totalSize.BitLen() > 63 {
+ return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
+ }
+
+ if totalSize.Cmp(outputLength) > 0 {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
+ }
+ start = int(bigOffsetEnd.Uint64())
+ length = int(lengthBig.Uint64())
+ return
+}
+
+// tuplePointsTo resolves the location reference for dynamic tuple.
+func tuplePointsTo(index int, output []byte) (start int, err error) {
+ offset := new(big.Int).SetBytes(output[index : index+32])
+ outputLen := big.NewInt(int64(len(output)))
+
+ if offset.Cmp(outputLen) > 0 {
+ return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
+ }
+ if offset.BitLen() > 63 {
+ return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
+ }
+ return int(offset.Uint64()), nil
+}
diff --git a/abigen/abi/utils.go b/abigen/abi/utils.go
new file mode 100644
index 0000000..e24df5b
--- /dev/null
+++ b/abigen/abi/utils.go
@@ -0,0 +1,41 @@
+// Copyright 2022 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 abi
+
+import "fmt"
+
+// ResolveNameConflict returns the next available name for a given thing.
+// This helper can be used for lots of purposes:
+//
+// - In solidity function overloading is supported, this function can fix
+// the name conflicts of overloaded functions.
+// - In golang binding generation, the parameter(in function, event, error,
+// and struct definition) name will be converted to camelcase style which
+// may eventually lead to name conflicts.
+//
+// Name conflicts are mostly resolved by adding number suffix.
+// e.g. if the abi contains Methods send, send1
+// ResolveNameConflict would return send2 for input send.
+func ResolveNameConflict(rawName string, used func(string) bool) string {
+ name := rawName
+ ok := used(name)
+ for idx := 0; ok; idx++ {
+ name = fmt.Sprintf("%s%d", rawName, idx)
+ ok = used(name)
+ }
+ return name
+}
diff --git a/abigen/compiler/helpers.go b/abigen/compiler/helpers.go
new file mode 100644
index 0000000..063fc10
--- /dev/null
+++ b/abigen/compiler/helpers.go
@@ -0,0 +1,45 @@
+// Copyright 2019 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 compiler wraps the Solidity and Vyper compiler executables (solc; vyper).
+package compiler
+
+// Contract contains information about a compiled contract, alongside its code and runtime code.
+type Contract struct {
+ Code string `json:"code"`
+ RuntimeCode string `json:"runtime-code"`
+ Info ContractInfo `json:"info"`
+ Hashes map[string]string `json:"hashes"`
+}
+
+// ContractInfo contains information about a compiled contract, including access
+// to the ABI definition, source mapping, user and developer docs, and metadata.
+//
+// Depending on the source, language version, compiler version, and compiler
+// options will provide information about how the contract was compiled.
+type ContractInfo struct {
+ Source string `json:"source"`
+ Language string `json:"language"`
+ LanguageVersion string `json:"languageVersion"`
+ CompilerVersion string `json:"compilerVersion"`
+ CompilerOptions string `json:"compilerOptions"`
+ SrcMap interface{} `json:"srcMap"`
+ SrcMapRuntime string `json:"srcMapRuntime"`
+ AbiDefinition interface{} `json:"abiDefinition"`
+ UserDoc interface{} `json:"userDoc"`
+ DeveloperDoc interface{} `json:"developerDoc"`
+ Metadata string `json:"metadata"`
+}
diff --git a/abigen/compiler/solidity.go b/abigen/compiler/solidity.go
new file mode 100644
index 0000000..ad8a44a
--- /dev/null
+++ b/abigen/compiler/solidity.go
@@ -0,0 +1,129 @@
+// Copyright 2015 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 compiler wraps the ABI compilation outputs.
+package compiler
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+// --combined-output format
+type solcOutput struct {
+ Contracts map[string]struct {
+ BinRuntime string `json:"bin-runtime"`
+ SrcMapRuntime string `json:"srcmap-runtime"`
+ Bin, SrcMap, Abi, Devdoc, Userdoc, Metadata string
+ Hashes map[string]string
+ }
+ Version string
+}
+
+// solidity v.0.8 changes the way ABI, Devdoc and Userdoc are serialized
+type solcOutputV8 struct {
+ Contracts map[string]struct {
+ BinRuntime string `json:"bin-runtime"`
+ SrcMapRuntime string `json:"srcmap-runtime"`
+ Bin, SrcMap, Metadata string
+ Abi interface{}
+ Devdoc interface{}
+ Userdoc interface{}
+ Hashes map[string]string
+ }
+ Version string
+}
+
+// ParseCombinedJSON takes the direct output of a solc --combined-output run and
+// parses it into a map of string contract name to Contract structs. The
+// provided source, language and compiler version, and compiler options are all
+// passed through into the Contract structs.
+//
+// The solc output is expected to contain ABI, source mapping, user docs, and dev docs.
+//
+// Returns an error if the JSON is malformed or missing data, or if the JSON
+// embedded within the JSON is malformed.
+func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) {
+ var output solcOutput
+ if err := json.Unmarshal(combinedJSON, &output); err != nil {
+ // Try to parse the output with the new solidity v.0.8.0 rules
+ return parseCombinedJSONV8(combinedJSON, source, languageVersion, compilerVersion, compilerOptions)
+ }
+ // Compilation succeeded, assemble and return the contracts.
+ contracts := make(map[string]*Contract)
+ for name, info := range output.Contracts {
+ // Parse the individual compilation results.
+ var abi interface{}
+ if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil {
+ return nil, fmt.Errorf("solc: error reading abi definition (%v)", err)
+ }
+ var userdoc, devdoc interface{}
+ json.Unmarshal([]byte(info.Userdoc), &userdoc)
+ json.Unmarshal([]byte(info.Devdoc), &devdoc)
+
+ contracts[name] = &Contract{
+ Code: "0x" + info.Bin,
+ RuntimeCode: "0x" + info.BinRuntime,
+ Hashes: info.Hashes,
+ Info: ContractInfo{
+ Source: source,
+ Language: "Solidity",
+ LanguageVersion: languageVersion,
+ CompilerVersion: compilerVersion,
+ CompilerOptions: compilerOptions,
+ SrcMap: info.SrcMap,
+ SrcMapRuntime: info.SrcMapRuntime,
+ AbiDefinition: abi,
+ UserDoc: userdoc,
+ DeveloperDoc: devdoc,
+ Metadata: info.Metadata,
+ },
+ }
+ }
+ return contracts, nil
+}
+
+// parseCombinedJSONV8 parses the direct output of solc --combined-output
+// and parses it using the rules from solidity v.0.8.0 and later.
+func parseCombinedJSONV8(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) {
+ var output solcOutputV8
+ if err := json.Unmarshal(combinedJSON, &output); err != nil {
+ return nil, err
+ }
+ // Compilation succeeded, assemble and return the contracts.
+ contracts := make(map[string]*Contract)
+ for name, info := range output.Contracts {
+ contracts[name] = &Contract{
+ Code: "0x" + info.Bin,
+ RuntimeCode: "0x" + info.BinRuntime,
+ Hashes: info.Hashes,
+ Info: ContractInfo{
+ Source: source,
+ Language: "Solidity",
+ LanguageVersion: languageVersion,
+ CompilerVersion: compilerVersion,
+ CompilerOptions: compilerOptions,
+ SrcMap: info.SrcMap,
+ SrcMapRuntime: info.SrcMapRuntime,
+ AbiDefinition: info.Abi,
+ UserDoc: info.Userdoc,
+ DeveloperDoc: info.Devdoc,
+ Metadata: info.Metadata,
+ },
+ }
+ }
+ return contracts, nil
+}
diff --git a/abigen/flags/helpers.go b/abigen/flags/helpers.go
new file mode 100644
index 0000000..5a0326e
--- /dev/null
+++ b/abigen/flags/helpers.go
@@ -0,0 +1,176 @@
+// Copyright 2020 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 flags
+
+import (
+ "os"
+ "path/filepath"
+
+ "gopkg.in/urfave/cli.v1"
+)
+
+const (
+ Version = "1.10.20"
+ VersionMeta = "stable"
+)
+
+var (
+ CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} {{.cmd.ArgsUsage}}
+{{if .cmd.Description}}{{.cmd.Description}}
+{{end}}{{if .cmd.Subcommands}}
+SUBCOMMANDS:
+ {{range .cmd.Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
+ {{end}}{{end}}{{if .categorizedFlags}}
+{{range $idx, $categorized := .categorizedFlags}}{{$categorized.Name}} OPTIONS:
+{{range $categorized.Flags}}{{"\t"}}{{.}}
+{{end}}
+{{end}}{{end}}`
+
+ OriginCommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} {{.ArgsUsage}}
+{{if .Description}}{{.Description}}
+{{end}}{{if .Subcommands}}
+SUBCOMMANDS:
+ {{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
+ {{end}}{{end}}{{if .Flags}}
+OPTIONS:
+{{range $.Flags}} {{.}}
+{{end}}
+{{end}}`
+
+ // AppHelpTemplate is the test template for the default, global app help topic.
+ AppHelpTemplate = `NAME:
+ {{.App.Name}} - {{.App.Usage}}
+
+ Copyright 2013-2022 The go-ethereum Authors
+
+USAGE:
+ {{.App.HelpName}} [options]{{if .App.Commands}} [command] [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
+ {{if .App.Version}}
+VERSION:
+ {{.App.Version}}
+ {{end}}{{if len .App.Authors}}
+AUTHOR(S):
+ {{range .App.Authors}}{{ . }}{{end}}
+ {{end}}{{if .App.Commands}}
+COMMANDS:
+ {{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
+ {{end}}{{end}}{{if .FlagGroups}}
+{{range .FlagGroups}}{{.Name}} OPTIONS:
+ {{range .Flags}}{{.}}
+ {{end}}
+{{end}}{{end}}{{if .App.Copyright }}
+COPYRIGHT:
+ {{.App.Copyright}}
+ {{end}}
+`
+ // ClefAppHelpTemplate is the template for the default, global app help topic.
+ ClefAppHelpTemplate = `NAME:
+ {{.App.Name}} - {{.App.Usage}}
+
+ Copyright 2013-2022 The go-ethereum Authors
+
+USAGE:
+ {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
+ {{if .App.Version}}
+COMMANDS:
+ {{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
+ {{end}}{{end}}{{if .FlagGroups}}
+{{range .FlagGroups}}{{.Name}} OPTIONS:
+ {{range .Flags}}{{.}}
+ {{end}}
+{{end}}{{end}}{{if .App.Copyright }}
+COPYRIGHT:
+ {{.App.Copyright}}
+ {{end}}
+`
+)
+
+// HelpData is a one shot struct to pass to the usage template
+type HelpData struct {
+ App interface{}
+ FlagGroups []FlagGroup
+}
+
+// FlagGroup is a collection of flags belonging to a single topic.
+type FlagGroup struct {
+ Name string
+ Flags []cli.Flag
+}
+
+// ByCategory sorts an array of FlagGroup by Name in the order
+// defined in AppHelpFlagGroups.
+type ByCategory []FlagGroup
+
+func (a ByCategory) Len() int { return len(a) }
+func (a ByCategory) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a ByCategory) Less(i, j int) bool {
+ iCat, jCat := a[i].Name, a[j].Name
+ iIdx, jIdx := len(a), len(a) // ensure non categorized flags come last
+
+ for i, group := range a {
+ if iCat == group.Name {
+ iIdx = i
+ }
+ if jCat == group.Name {
+ jIdx = i
+ }
+ }
+
+ return iIdx < jIdx
+}
+
+func FlagCategory(flag cli.Flag, flagGroups []FlagGroup) string {
+ for _, category := range flagGroups {
+ for _, flg := range category.Flags {
+ if flg.GetName() == flag.GetName() {
+ return category.Name
+ }
+ }
+ }
+ return "MISC"
+}
+
+// NewApp creates an app with sane defaults.
+func NewApp(gitCommit, gitDate, usage string) *cli.App {
+ app := cli.NewApp()
+ app.EnableBashCompletion = true
+ app.Name = filepath.Base(os.Args[0])
+ app.Author = ""
+ app.Email = ""
+ app.Version = VersionWithCommit(gitCommit, gitDate)
+ app.Usage = usage
+ return app
+}
+
+var VersionWithMeta = func() string {
+ v := Version
+ if VersionMeta != "" {
+ v += "-" + VersionMeta
+ }
+ return v
+}()
+
+func VersionWithCommit(gitCommit, gitDate string) string {
+ vsn := VersionWithMeta
+ if len(gitCommit) >= 8 {
+ vsn += "-" + gitCommit[:8]
+ }
+ if (VersionMeta != "stable") && (gitDate != "") {
+ vsn += "-" + gitDate
+ }
+ return vsn
+}
diff --git a/abigen/main.go b/abigen/main.go
new file mode 100644
index 0000000..4de336f
--- /dev/null
+++ b/abigen/main.go
@@ -0,0 +1,244 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "regexp"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/abigen/abi/bind"
+ "github.com/ChainSQL/go-chainsql-api/abigen/compiler"
+ "github.com/ChainSQL/go-chainsql-api/abigen/flags"
+ "github.com/ChainSQL/go-chainsql-api/abigen/utils"
+ "github.com/ChainSQL/go-chainsql-api/crypto"
+ "gopkg.in/urfave/cli.v1"
+)
+
+var (
+ // Git SHA1 commit hash of the release (set via linker flags)
+ gitCommit = ""
+ gitDate = ""
+
+ app *cli.App
+
+ // Flags needed by abigen
+ abiFlag = cli.StringFlag{
+ Name: "abi",
+ Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
+ }
+ binFlag = cli.StringFlag{
+ Name: "bin",
+ Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
+ }
+ typeFlag = cli.StringFlag{
+ Name: "type",
+ Usage: "Struct name for the binding (default = package name)",
+ }
+ jsonFlag = cli.StringFlag{
+ Name: "combined-json",
+ Usage: "Path to the combined-json file generated by compiler, - for STDIN",
+ }
+ excFlag = cli.StringFlag{
+ Name: "exc",
+ Usage: "Comma separated types to exclude from binding",
+ }
+ pkgFlag = cli.StringFlag{
+ Name: "pkg",
+ Usage: "Package name to generate the binding into",
+ }
+ outFlag = cli.StringFlag{
+ Name: "out",
+ Usage: "Output file for the generated binding (default = stdout)",
+ }
+ langFlag = cli.StringFlag{
+ Name: "lang",
+ Usage: "Destination language for the bindings (go, java, objc)",
+ Value: "go",
+ }
+ aliasFlag = cli.StringFlag{
+ Name: "alias",
+ Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2",
+ }
+)
+
+func init() {
+ app = flags.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
+ app.Flags = []cli.Flag{
+ abiFlag,
+ binFlag,
+ typeFlag,
+ jsonFlag,
+ excFlag,
+ pkgFlag,
+ outFlag,
+ langFlag,
+ aliasFlag,
+ }
+ app.Action = utils.MigrateFlags(abigen)
+ cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
+}
+
+func abigen(c *cli.Context) error {
+ utils.CheckExclusive(c, abiFlag, jsonFlag) // Only one source can be selected.
+ if c.GlobalString(pkgFlag.Name) == "" {
+ log.Printf("No destination package specified (--pkg)")
+ }
+ var lang bind.Lang
+ switch c.GlobalString(langFlag.Name) {
+ case "go":
+ lang = bind.LangGo
+ case "java":
+ lang = bind.LangJava
+ case "objc":
+ lang = bind.LangObjC
+ log.Printf("Objc binding generation is uncompleted")
+ default:
+ log.Printf("Unsupported destination language \"%s\" (--lang)", c.GlobalString(langFlag.Name))
+ }
+ // If the entire solidity code was specified, build and bind based on that
+ var (
+ abis []string
+ bins []string
+ types []string
+ sigs []map[string]string
+ libs = make(map[string]string)
+ aliases = make(map[string]string)
+ )
+ if c.GlobalString(abiFlag.Name) != "" {
+ // Load up the ABI, optional bytecode and type name from the parameters
+ var (
+ abi []byte
+ err error
+ )
+ input := c.GlobalString(abiFlag.Name)
+ if input == "-" {
+ abi, err = io.ReadAll(os.Stdin)
+ } else {
+ abi, err = os.ReadFile(input)
+ }
+ if err != nil {
+ log.Printf("Failed to read input ABI: %v", err)
+ }
+ abis = append(abis, string(abi))
+
+ var bin []byte
+ if binFile := c.GlobalString(binFlag.Name); binFile != "" {
+ if bin, err = os.ReadFile(binFile); err != nil {
+ log.Printf("Failed to read input bytecode: %v", err)
+ }
+ if strings.Contains(string(bin), "//") {
+ log.Printf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
+ }
+ }
+ bins = append(bins, string(bin))
+
+ kind := c.GlobalString(typeFlag.Name)
+ if kind == "" {
+ kind = c.GlobalString(pkgFlag.Name)
+ }
+ types = append(types, kind)
+ } else {
+ // Generate the list of types to exclude from binding
+ exclude := make(map[string]bool)
+ for _, kind := range strings.Split(c.GlobalString(excFlag.Name), ",") {
+ exclude[strings.ToLower(kind)] = true
+ }
+ var contracts map[string]*compiler.Contract
+
+ if c.GlobalIsSet(jsonFlag.Name) {
+ var (
+ input = c.GlobalString(jsonFlag.Name)
+ jsonOutput []byte
+ err error
+ )
+ if input == "-" {
+ jsonOutput, err = io.ReadAll(os.Stdin)
+ } else {
+ jsonOutput, err = os.ReadFile(input)
+ }
+ if err != nil {
+ log.Printf("Failed to read combined-json: %v", err)
+ }
+ contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
+ if err != nil {
+ log.Printf("Failed to read contract information from json output: %v", err)
+ }
+ }
+ // Gather all non-excluded contract for binding
+ for name, contract := range contracts {
+ if exclude[strings.ToLower(name)] {
+ continue
+ }
+ abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
+ if err != nil {
+ log.Printf("Failed to parse ABIs from compiler output: %v", err)
+ }
+ abis = append(abis, string(abi))
+ bins = append(bins, contract.Code)
+ sigs = append(sigs, contract.Hashes)
+ nameParts := strings.Split(name, ":")
+ types = append(types, nameParts[len(nameParts)-1])
+
+ // Derive the library placeholder which is a 34 character prefix of the
+ // hex encoding of the keccak256 hash of the fully qualified library name.
+ // Note that the fully qualified library name is the path of its source
+ // file and the library name separated by ":".
+ libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] // the first 2 chars are 0x
+ libs[libPattern] = nameParts[len(nameParts)-1]
+ }
+ }
+ // Extract all aliases from the flags
+ if c.GlobalIsSet(aliasFlag.Name) {
+ // We support multi-versions for aliasing
+ // e.g.
+ // foo=bar,foo2=bar2
+ // foo:bar,foo2:bar2
+ re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
+ submatches := re.FindAllStringSubmatch(c.GlobalString(aliasFlag.Name), -1)
+ for _, match := range submatches {
+ aliases[match[1]] = match[2]
+ }
+ }
+ // Generate the contract binding
+ code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases)
+ if err != nil {
+ log.Printf("Failed to generate ABI binding: %v", err)
+ }
+ // Either flush it out to a file or display on the standard output
+ if !c.GlobalIsSet(outFlag.Name) {
+ fmt.Printf("%s\n", code)
+ return nil
+ }
+ if err := os.WriteFile(c.GlobalString(outFlag.Name), []byte(code), 0600); err != nil {
+ log.Printf("Failed to write ABI binding: %v", err)
+ }
+ return nil
+}
+
+func main() {
+ // log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+
+ if err := app.Run(os.Args); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
diff --git a/abigen/utils/util.go b/abigen/utils/util.go
new file mode 100644
index 0000000..b212494
--- /dev/null
+++ b/abigen/utils/util.go
@@ -0,0 +1,61 @@
+package utils
+
+import (
+ "fmt"
+ "log"
+ "strings"
+
+ "gopkg.in/urfave/cli.v1"
+)
+
+// CheckExclusive verifies that only a single instance of the provided flags was
+// set by the user. Each flag might optionally be followed by a string type to
+// specialize it further.
+func CheckExclusive(ctx *cli.Context, args ...interface{}) {
+ set := make([]string, 0, 1)
+ for i := 0; i < len(args); i++ {
+ // Make sure the next argument is a flag and skip if not set
+ flag, ok := args[i].(cli.Flag)
+ if !ok {
+ panic(fmt.Sprintf("invalid argument, not cli.Flag type: %T", args[i]))
+ }
+ // Check if next arg extends current and expand its name if so
+ name := flag.GetName()
+
+ if i+1 < len(args) {
+ switch option := args[i+1].(type) {
+ case string:
+ // Extended flag check, make sure value set doesn't conflict with passed in option
+ if ctx.GlobalString(flag.GetName()) == option {
+ name += "=" + option
+ set = append(set, "--"+name)
+ }
+ // shift arguments and continue
+ i++
+ continue
+
+ case cli.Flag:
+ default:
+ panic(fmt.Sprintf("invalid argument, not cli.Flag or string extension: %T", args[i+1]))
+ }
+ }
+ // Mark the flag if it's set
+ if ctx.GlobalIsSet(flag.GetName()) {
+ set = append(set, "--"+name)
+ }
+ }
+ if len(set) > 1 {
+ log.Printf("Flags %v can't be used at the same time", strings.Join(set, ", "))
+ }
+}
+
+func MigrateFlags(action func(ctx *cli.Context) error) func(*cli.Context) error {
+ return func(ctx *cli.Context) error {
+ for _, name := range ctx.FlagNames() {
+ if ctx.IsSet(name) {
+ ctx.GlobalSet(name, ctx.String(name))
+ }
+ }
+ return action(ctx)
+ }
+}
diff --git a/common/big.go b/common/big.go
new file mode 100644
index 0000000..65d4377
--- /dev/null
+++ b/common/big.go
@@ -0,0 +1,30 @@
+// 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 common
+
+import "math/big"
+
+// Common big integers often used
+var (
+ Big1 = big.NewInt(1)
+ Big2 = big.NewInt(2)
+ Big3 = big.NewInt(3)
+ Big0 = big.NewInt(0)
+ Big32 = big.NewInt(32)
+ Big256 = big.NewInt(256)
+ Big257 = big.NewInt(257)
+)
diff --git a/common/bytes.go b/common/bytes.go
new file mode 100644
index 0000000..288d9ea
--- /dev/null
+++ b/common/bytes.go
@@ -0,0 +1,151 @@
+// 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 common contains various helper functions.
+package common
+
+import (
+ "encoding/hex"
+ "errors"
+
+ "github.com/ChainSQL/go-chainsql-api/common/hexutil"
+)
+
+// FromHex returns the bytes represented by the hexadecimal string s.
+// s may be prefixed with "0x".
+func FromHex(s string) []byte {
+ if has0xPrefix(s) {
+ s = s[2:]
+ }
+ if len(s)%2 == 1 {
+ s = "0" + s
+ }
+ return Hex2Bytes(s)
+}
+
+// CopyBytes returns an exact copy of the provided bytes.
+func CopyBytes(b []byte) (copiedBytes []byte) {
+ if b == nil {
+ return nil
+ }
+ copiedBytes = make([]byte, len(b))
+ copy(copiedBytes, b)
+
+ return
+}
+
+// has0xPrefix validates str begins with '0x' or '0X'.
+func has0xPrefix(str string) bool {
+ return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
+}
+
+// isHexCharacter returns bool of c being a valid hexadecimal.
+func isHexCharacter(c byte) bool {
+ return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
+}
+
+// isHex validates whether each byte is valid hexadecimal string.
+func isHex(str string) bool {
+ if len(str)%2 != 0 {
+ return false
+ }
+ for _, c := range []byte(str) {
+ if !isHexCharacter(c) {
+ return false
+ }
+ }
+ return true
+}
+
+// Bytes2Hex returns the hexadecimal encoding of d.
+func Bytes2Hex(d []byte) string {
+ return hex.EncodeToString(d)
+}
+
+// Hex2Bytes returns the bytes represented by the hexadecimal string str.
+func Hex2Bytes(str string) []byte {
+ h, _ := hex.DecodeString(str)
+ return h
+}
+
+// Hex2BytesFixed returns bytes of a specified fixed length flen.
+func Hex2BytesFixed(str string, flen int) []byte {
+ h, _ := hex.DecodeString(str)
+ if len(h) == flen {
+ return h
+ }
+ if len(h) > flen {
+ return h[len(h)-flen:]
+ }
+ hh := make([]byte, flen)
+ copy(hh[flen-len(h):flen], h)
+ return hh
+}
+
+// ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes
+func ParseHexOrString(str string) ([]byte, error) {
+ b, err := hexutil.Decode(str)
+ if errors.Is(err, hexutil.ErrMissingPrefix) {
+ return []byte(str), nil
+ }
+ return b, err
+}
+
+// RightPadBytes zero-pads slice to the right up to length l.
+func RightPadBytes(slice []byte, l int) []byte {
+ if l <= len(slice) {
+ return slice
+ }
+
+ padded := make([]byte, l)
+ copy(padded, slice)
+
+ return padded
+}
+
+// LeftPadBytes zero-pads slice to the left up to length l.
+func LeftPadBytes(slice []byte, l int) []byte {
+ if l <= len(slice) {
+ return slice
+ }
+
+ padded := make([]byte, l)
+ copy(padded[l-len(slice):], slice)
+
+ return padded
+}
+
+// TrimLeftZeroes returns a subslice of s without leading zeroes
+func TrimLeftZeroes(s []byte) []byte {
+ idx := 0
+ for ; idx < len(s); idx++ {
+ if s[idx] != 0 {
+ break
+ }
+ }
+ return s[idx:]
+}
+
+// TrimRightZeroes returns a subslice of s without trailing zeroes
+func TrimRightZeroes(s []byte) []byte {
+ idx := len(s)
+ for ; idx > 0; idx-- {
+ if s[idx-1] != 0 {
+ break
+ }
+ }
+ return s[:idx]
+}
diff --git a/common/common.go b/common/common.go
index dacae18..2111e47 100644
--- a/common/common.go
+++ b/common/common.go
@@ -1,5 +1,29 @@
package common
+type KeyType int
+
+const (
+ ECDSA KeyType = 0
+ Ed25519 KeyType = 1
+ SoftGMAlg KeyType = 2
+)
+
+/*func (keyType KeyType) String() string {
+ switch keyType {
+ case secp256k1:
+ return "secp256k1"
+ case Ed25519:
+ return "Ed25519"
+ default:
+ return "unknown key type"
+ }
+}*/
+
+/*func (keyType KeyType) MarshalText() ([]byte, error) {
+
+ return []byte(keyType.String()), nil
+}*/
+
// Auth is the type with ws connection infomations
type Auth struct {
Address string
@@ -7,18 +31,38 @@ type Auth struct {
Owner string
}
+// TxResult is tx submit response
+type TxResult struct {
+ Status string `json:"status"`
+ TxHash string `json:"hash"`
+ ErrorCode string `json:"error,omitempty"`
+ ErrorMessage string `json:"errorMessage,omitempty"`
+}
+
//IRequest define interface for request
type IRequest interface {
GetID() int64
+ SetID(inputID int64)
+ SetSchemaID(schemaID string) *RequestBase
}
//RequestBase contains fields that all requests will have
type RequestBase struct {
- Command string `json:"command"`
- ID int64 `json:"id,omitempty"`
+ Command string `json:"command"`
+ ID int64 `json:"id,omitempty"`
+ SchemaID string `json:"schema_id"`
}
// GetID return id for request
func (r *RequestBase) GetID() int64 {
return r.ID
}
+
+func (r *RequestBase) SetID(inputId int64) {
+ r.ID = inputId
+}
+
+func (r *RequestBase) SetSchemaID(schemaID string) *RequestBase {
+ r.SchemaID = schemaID
+ return r
+}
diff --git a/common/hexutil/hexutil.go b/common/hexutil/hexutil.go
new file mode 100644
index 0000000..e0241f5
--- /dev/null
+++ b/common/hexutil/hexutil.go
@@ -0,0 +1,241 @@
+// Copyright 2016 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 hexutil implements hex encoding with 0x prefix.
+This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads.
+
+Encoding Rules
+
+All hex data must have prefix "0x".
+
+For byte slices, the hex data must be of even length. An empty byte slice
+encodes as "0x".
+
+Integers are encoded using the least amount of digits (no leading zero digits). Their
+encoding may be of uneven length. The number zero encodes as "0x0".
+*/
+package hexutil
+
+import (
+ "encoding/hex"
+ "fmt"
+ "math/big"
+ "strconv"
+)
+
+const uintBits = 32 << (uint64(^uint(0)) >> 63)
+
+// Errors
+var (
+ ErrEmptyString = &decError{"empty hex string"}
+ ErrSyntax = &decError{"invalid hex string"}
+ ErrMissingPrefix = &decError{"hex string without 0x prefix"}
+ ErrOddLength = &decError{"hex string of odd length"}
+ ErrEmptyNumber = &decError{"hex string \"0x\""}
+ ErrLeadingZero = &decError{"hex number with leading zero digits"}
+ ErrUint64Range = &decError{"hex number > 64 bits"}
+ ErrUintRange = &decError{fmt.Sprintf("hex number > %d bits", uintBits)}
+ ErrBig256Range = &decError{"hex number > 256 bits"}
+)
+
+type decError struct{ msg string }
+
+func (err decError) Error() string { return err.msg }
+
+// Decode decodes a hex string with 0x prefix.
+func Decode(input string) ([]byte, error) {
+ if len(input) == 0 {
+ return nil, ErrEmptyString
+ }
+ if !has0xPrefix(input) {
+ return nil, ErrMissingPrefix
+ }
+ b, err := hex.DecodeString(input[2:])
+ if err != nil {
+ err = mapError(err)
+ }
+ return b, err
+}
+
+// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
+func MustDecode(input string) []byte {
+ dec, err := Decode(input)
+ if err != nil {
+ panic(err)
+ }
+ return dec
+}
+
+// Encode encodes b as a hex string with 0x prefix.
+func Encode(b []byte) string {
+ enc := make([]byte, len(b)*2+2)
+ copy(enc, "0x")
+ hex.Encode(enc[2:], b)
+ return string(enc)
+}
+
+// DecodeUint64 decodes a hex string with 0x prefix as a quantity.
+func DecodeUint64(input string) (uint64, error) {
+ raw, err := checkNumber(input)
+ if err != nil {
+ return 0, err
+ }
+ dec, err := strconv.ParseUint(raw, 16, 64)
+ if err != nil {
+ err = mapError(err)
+ }
+ return dec, err
+}
+
+// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity.
+// It panics for invalid input.
+func MustDecodeUint64(input string) uint64 {
+ dec, err := DecodeUint64(input)
+ if err != nil {
+ panic(err)
+ }
+ return dec
+}
+
+// EncodeUint64 encodes i as a hex string with 0x prefix.
+func EncodeUint64(i uint64) string {
+ enc := make([]byte, 2, 10)
+ copy(enc, "0x")
+ return string(strconv.AppendUint(enc, i, 16))
+}
+
+var bigWordNibbles int
+
+func init() {
+ // This is a weird way to compute the number of nibbles required for big.Word.
+ // The usual way would be to use constant arithmetic but go vet can't handle that.
+ b, _ := new(big.Int).SetString("FFFFFFFFFF", 16)
+ switch len(b.Bits()) {
+ case 1:
+ bigWordNibbles = 16
+ case 2:
+ bigWordNibbles = 8
+ default:
+ panic("weird big.Word size")
+ }
+}
+
+// DecodeBig decodes a hex string with 0x prefix as a quantity.
+// Numbers larger than 256 bits are not accepted.
+func DecodeBig(input string) (*big.Int, error) {
+ raw, err := checkNumber(input)
+ if err != nil {
+ return nil, err
+ }
+ if len(raw) > 64 {
+ return nil, ErrBig256Range
+ }
+ words := make([]big.Word, len(raw)/bigWordNibbles+1)
+ end := len(raw)
+ for i := range words {
+ start := end - bigWordNibbles
+ if start < 0 {
+ start = 0
+ }
+ for ri := start; ri < end; ri++ {
+ nib := decodeNibble(raw[ri])
+ if nib == badNibble {
+ return nil, ErrSyntax
+ }
+ words[i] *= 16
+ words[i] += big.Word(nib)
+ }
+ end = start
+ }
+ dec := new(big.Int).SetBits(words)
+ return dec, nil
+}
+
+// MustDecodeBig decodes a hex string with 0x prefix as a quantity.
+// It panics for invalid input.
+func MustDecodeBig(input string) *big.Int {
+ dec, err := DecodeBig(input)
+ if err != nil {
+ panic(err)
+ }
+ return dec
+}
+
+// EncodeBig encodes bigint as a hex string with 0x prefix.
+func EncodeBig(bigint *big.Int) string {
+ if sign := bigint.Sign(); sign == 0 {
+ return "0x0"
+ } else if sign > 0 {
+ return "0x" + bigint.Text(16)
+ } else {
+ return "-0x" + bigint.Text(16)[1:]
+ }
+}
+
+func has0xPrefix(input string) bool {
+ return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
+}
+
+func checkNumber(input string) (raw string, err error) {
+ if len(input) == 0 {
+ return "", ErrEmptyString
+ }
+ if !has0xPrefix(input) {
+ return "", ErrMissingPrefix
+ }
+ input = input[2:]
+ if len(input) == 0 {
+ return "", ErrEmptyNumber
+ }
+ if len(input) > 1 && input[0] == '0' {
+ return "", ErrLeadingZero
+ }
+ return input, nil
+}
+
+const badNibble = ^uint64(0)
+
+func decodeNibble(in byte) uint64 {
+ switch {
+ case in >= '0' && in <= '9':
+ return uint64(in - '0')
+ case in >= 'A' && in <= 'F':
+ return uint64(in - 'A' + 10)
+ case in >= 'a' && in <= 'f':
+ return uint64(in - 'a' + 10)
+ default:
+ return badNibble
+ }
+}
+
+func mapError(err error) error {
+ if err, ok := err.(*strconv.NumError); ok {
+ switch err.Err {
+ case strconv.ErrRange:
+ return ErrUint64Range
+ case strconv.ErrSyntax:
+ return ErrSyntax
+ }
+ }
+ if _, ok := err.(hex.InvalidByteError); ok {
+ return ErrSyntax
+ }
+ if err == hex.ErrLength {
+ return ErrOddLength
+ }
+ return err
+}
diff --git a/common/hexutil/json.go b/common/hexutil/json.go
new file mode 100644
index 0000000..50db208
--- /dev/null
+++ b/common/hexutil/json.go
@@ -0,0 +1,376 @@
+// Copyright 2016 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 hexutil
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "math/big"
+ "reflect"
+ "strconv"
+)
+
+var (
+ bytesT = reflect.TypeOf(Bytes(nil))
+ bigT = reflect.TypeOf((*Big)(nil))
+ uintT = reflect.TypeOf(Uint(0))
+ uint64T = reflect.TypeOf(Uint64(0))
+)
+
+// Bytes marshals/unmarshals as a JSON string with 0x prefix.
+// The empty slice marshals as "0x".
+type Bytes []byte
+
+// MarshalText implements encoding.TextMarshaler
+func (b Bytes) MarshalText() ([]byte, error) {
+ result := make([]byte, len(b)*2+2)
+ copy(result, `0x`)
+ hex.Encode(result[2:], b)
+ return result, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Bytes) UnmarshalJSON(input []byte) error {
+ if !isString(input) {
+ return errNonString(bytesT)
+ }
+ return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bytesT)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (b *Bytes) UnmarshalText(input []byte) error {
+ raw, err := checkText(input, true)
+ if err != nil {
+ return err
+ }
+ dec := make([]byte, len(raw)/2)
+ if _, err = hex.Decode(dec, raw); err != nil {
+ err = mapError(err)
+ } else {
+ *b = dec
+ }
+ return err
+}
+
+// String returns the hex encoding of b.
+func (b Bytes) String() string {
+ return Encode(b)
+}
+
+// ImplementsGraphQLType returns true if Bytes implements the specified GraphQL type.
+func (b Bytes) ImplementsGraphQLType(name string) bool { return name == "Bytes" }
+
+// UnmarshalGraphQL unmarshals the provided GraphQL query data.
+func (b *Bytes) UnmarshalGraphQL(input interface{}) error {
+ var err error
+ switch input := input.(type) {
+ case string:
+ data, err := Decode(input)
+ if err != nil {
+ return err
+ }
+ *b = data
+ default:
+ err = fmt.Errorf("unexpected type %T for Bytes", input)
+ }
+ return err
+}
+
+// UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out
+// determines the required input length. This function is commonly used to implement the
+// UnmarshalJSON method for fixed-size types.
+func UnmarshalFixedJSON(typ reflect.Type, input, out []byte) error {
+ if !isString(input) {
+ return errNonString(typ)
+ }
+ return wrapTypeError(UnmarshalFixedText(typ.String(), input[1:len(input)-1], out), typ)
+}
+
+// UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out
+// determines the required input length. This function is commonly used to implement the
+// UnmarshalText method for fixed-size types.
+func UnmarshalFixedText(typname string, input, out []byte) error {
+ raw, err := checkText(input, true)
+ if err != nil {
+ return err
+ }
+ if len(raw)/2 != len(out) {
+ return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
+ }
+ // Pre-verify syntax before modifying out.
+ for _, b := range raw {
+ if decodeNibble(b) == badNibble {
+ return ErrSyntax
+ }
+ }
+ hex.Decode(out, raw)
+ return nil
+}
+
+// UnmarshalFixedUnprefixedText decodes the input as a string with optional 0x prefix. The
+// length of out determines the required input length. This function is commonly used to
+// implement the UnmarshalText method for fixed-size types.
+func UnmarshalFixedUnprefixedText(typname string, input, out []byte) error {
+ raw, err := checkText(input, false)
+ if err != nil {
+ return err
+ }
+ if len(raw)/2 != len(out) {
+ return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
+ }
+ // Pre-verify syntax before modifying out.
+ for _, b := range raw {
+ if decodeNibble(b) == badNibble {
+ return ErrSyntax
+ }
+ }
+ hex.Decode(out, raw)
+ return nil
+}
+
+// Big marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+//
+// Negative integers are not supported at this time. Attempting to marshal them will
+// return an error. Values larger than 256bits are rejected by Unmarshal but will be
+// marshaled without error.
+type Big big.Int
+
+// MarshalText implements encoding.TextMarshaler
+func (b Big) MarshalText() ([]byte, error) {
+ return []byte(EncodeBig((*big.Int)(&b))), nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Big) UnmarshalJSON(input []byte) error {
+ if !isString(input) {
+ return errNonString(bigT)
+ }
+ return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), bigT)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler
+func (b *Big) UnmarshalText(input []byte) error {
+ raw, err := checkNumberText(input)
+ if err != nil {
+ return err
+ }
+ if len(raw) > 64 {
+ return ErrBig256Range
+ }
+ words := make([]big.Word, len(raw)/bigWordNibbles+1)
+ end := len(raw)
+ for i := range words {
+ start := end - bigWordNibbles
+ if start < 0 {
+ start = 0
+ }
+ for ri := start; ri < end; ri++ {
+ nib := decodeNibble(raw[ri])
+ if nib == badNibble {
+ return ErrSyntax
+ }
+ words[i] *= 16
+ words[i] += big.Word(nib)
+ }
+ end = start
+ }
+ var dec big.Int
+ dec.SetBits(words)
+ *b = (Big)(dec)
+ return nil
+}
+
+// ToInt converts b to a big.Int.
+func (b *Big) ToInt() *big.Int {
+ return (*big.Int)(b)
+}
+
+// String returns the hex encoding of b.
+func (b *Big) String() string {
+ return EncodeBig(b.ToInt())
+}
+
+// ImplementsGraphQLType returns true if Big implements the provided GraphQL type.
+func (b Big) ImplementsGraphQLType(name string) bool { return name == "BigInt" }
+
+// UnmarshalGraphQL unmarshals the provided GraphQL query data.
+func (b *Big) UnmarshalGraphQL(input interface{}) error {
+ var err error
+ switch input := input.(type) {
+ case string:
+ return b.UnmarshalText([]byte(input))
+ case int32:
+ var num big.Int
+ num.SetInt64(int64(input))
+ *b = Big(num)
+ default:
+ err = fmt.Errorf("unexpected type %T for BigInt", input)
+ }
+ return err
+}
+
+// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+type Uint64 uint64
+
+// MarshalText implements encoding.TextMarshaler.
+func (b Uint64) MarshalText() ([]byte, error) {
+ buf := make([]byte, 2, 10)
+ copy(buf, `0x`)
+ buf = strconv.AppendUint(buf, uint64(b), 16)
+ return buf, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Uint64) UnmarshalJSON(input []byte) error {
+ if !isString(input) {
+ return errNonString(uint64T)
+ }
+ return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uint64T)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler
+func (b *Uint64) UnmarshalText(input []byte) error {
+ raw, err := checkNumberText(input)
+ if err != nil {
+ return err
+ }
+ if len(raw) > 16 {
+ return ErrUint64Range
+ }
+ var dec uint64
+ for _, byte := range raw {
+ nib := decodeNibble(byte)
+ if nib == badNibble {
+ return ErrSyntax
+ }
+ dec *= 16
+ dec += nib
+ }
+ *b = Uint64(dec)
+ return nil
+}
+
+// String returns the hex encoding of b.
+func (b Uint64) String() string {
+ return EncodeUint64(uint64(b))
+}
+
+// ImplementsGraphQLType returns true if Uint64 implements the provided GraphQL type.
+func (b Uint64) ImplementsGraphQLType(name string) bool { return name == "Long" }
+
+// UnmarshalGraphQL unmarshals the provided GraphQL query data.
+func (b *Uint64) UnmarshalGraphQL(input interface{}) error {
+ var err error
+ switch input := input.(type) {
+ case string:
+ return b.UnmarshalText([]byte(input))
+ case int32:
+ *b = Uint64(input)
+ default:
+ err = fmt.Errorf("unexpected type %T for Long", input)
+ }
+ return err
+}
+
+// Uint marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+type Uint uint
+
+// MarshalText implements encoding.TextMarshaler.
+func (b Uint) MarshalText() ([]byte, error) {
+ return Uint64(b).MarshalText()
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *Uint) UnmarshalJSON(input []byte) error {
+ if !isString(input) {
+ return errNonString(uintT)
+ }
+ return wrapTypeError(b.UnmarshalText(input[1:len(input)-1]), uintT)
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (b *Uint) UnmarshalText(input []byte) error {
+ var u64 Uint64
+ err := u64.UnmarshalText(input)
+ if u64 > Uint64(^uint(0)) || err == ErrUint64Range {
+ return ErrUintRange
+ } else if err != nil {
+ return err
+ }
+ *b = Uint(u64)
+ return nil
+}
+
+// String returns the hex encoding of b.
+func (b Uint) String() string {
+ return EncodeUint64(uint64(b))
+}
+
+func isString(input []byte) bool {
+ return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
+}
+
+func bytesHave0xPrefix(input []byte) bool {
+ return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
+}
+
+func checkText(input []byte, wantPrefix bool) ([]byte, error) {
+ if len(input) == 0 {
+ return nil, nil // empty strings are allowed
+ }
+ if bytesHave0xPrefix(input) {
+ input = input[2:]
+ } else if wantPrefix {
+ return nil, ErrMissingPrefix
+ }
+ if len(input)%2 != 0 {
+ return nil, ErrOddLength
+ }
+ return input, nil
+}
+
+func checkNumberText(input []byte) (raw []byte, err error) {
+ if len(input) == 0 {
+ return nil, nil // empty strings are allowed
+ }
+ if !bytesHave0xPrefix(input) {
+ return nil, ErrMissingPrefix
+ }
+ input = input[2:]
+ if len(input) == 0 {
+ return nil, ErrEmptyNumber
+ }
+ if len(input) > 1 && input[0] == '0' {
+ return nil, ErrLeadingZero
+ }
+ return input, nil
+}
+
+func wrapTypeError(err error, typ reflect.Type) error {
+ if _, ok := err.(*decError); ok {
+ return &json.UnmarshalTypeError{Value: err.Error(), Type: typ}
+ }
+ return err
+}
+
+func errNonString(typ reflect.Type) error {
+ return &json.UnmarshalTypeError{Value: "non-string", Type: typ}
+}
diff --git a/common/math/big.go b/common/math/big.go
new file mode 100644
index 0000000..1af5b4d
--- /dev/null
+++ b/common/math/big.go
@@ -0,0 +1,259 @@
+// Copyright 2017 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 math provides integer math utilities.
+package math
+
+import (
+ "fmt"
+ "math/big"
+)
+
+// Various big integer limit values.
+var (
+ tt255 = BigPow(2, 255)
+ tt256 = BigPow(2, 256)
+ tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1))
+ tt63 = BigPow(2, 63)
+ MaxBig256 = new(big.Int).Set(tt256m1)
+ MaxBig63 = new(big.Int).Sub(tt63, big.NewInt(1))
+)
+
+const (
+ // number of bits in a big.Word
+ wordBits = 32 << (uint64(^big.Word(0)) >> 63)
+ // number of bytes in a big.Word
+ wordBytes = wordBits / 8
+)
+
+// HexOrDecimal256 marshals big.Int as hex or decimal.
+type HexOrDecimal256 big.Int
+
+// NewHexOrDecimal256 creates a new HexOrDecimal256
+func NewHexOrDecimal256(x int64) *HexOrDecimal256 {
+ b := big.NewInt(x)
+ h := HexOrDecimal256(*b)
+ return &h
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (i *HexOrDecimal256) UnmarshalText(input []byte) error {
+ bigint, ok := ParseBig256(string(input))
+ if !ok {
+ return fmt.Errorf("invalid hex or decimal integer %q", input)
+ }
+ *i = HexOrDecimal256(*bigint)
+ return nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+func (i *HexOrDecimal256) MarshalText() ([]byte, error) {
+ if i == nil {
+ return []byte("0x0"), nil
+ }
+ return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil
+}
+
+// Decimal256 unmarshals big.Int as a decimal string. When unmarshalling,
+// it however accepts either "0x"-prefixed (hex encoded) or non-prefixed (decimal)
+type Decimal256 big.Int
+
+// NewHexOrDecimal256 creates a new Decimal256
+func NewDecimal256(x int64) *Decimal256 {
+ b := big.NewInt(x)
+ d := Decimal256(*b)
+ return &d
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (i *Decimal256) UnmarshalText(input []byte) error {
+ bigint, ok := ParseBig256(string(input))
+ if !ok {
+ return fmt.Errorf("invalid hex or decimal integer %q", input)
+ }
+ *i = Decimal256(*bigint)
+ return nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+func (i *Decimal256) MarshalText() ([]byte, error) {
+ return []byte(i.String()), nil
+}
+
+// String implements Stringer.
+func (i *Decimal256) String() string {
+ if i == nil {
+ return "0"
+ }
+ return fmt.Sprintf("%#d", (*big.Int)(i))
+}
+
+// ParseBig256 parses s as a 256 bit integer in decimal or hexadecimal syntax.
+// Leading zeros are accepted. The empty string parses as zero.
+func ParseBig256(s string) (*big.Int, bool) {
+ if s == "" {
+ return new(big.Int), true
+ }
+ var bigint *big.Int
+ var ok bool
+ if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") {
+ bigint, ok = new(big.Int).SetString(s[2:], 16)
+ } else {
+ bigint, ok = new(big.Int).SetString(s, 10)
+ }
+ if ok && bigint.BitLen() > 256 {
+ bigint, ok = nil, false
+ }
+ return bigint, ok
+}
+
+// MustParseBig256 parses s as a 256 bit big integer and panics if the string is invalid.
+func MustParseBig256(s string) *big.Int {
+ v, ok := ParseBig256(s)
+ if !ok {
+ panic("invalid 256 bit integer: " + s)
+ }
+ return v
+}
+
+// BigPow returns a ** b as a big integer.
+func BigPow(a, b int64) *big.Int {
+ r := big.NewInt(a)
+ return r.Exp(r, big.NewInt(b), nil)
+}
+
+// BigMax returns the larger of x or y.
+func BigMax(x, y *big.Int) *big.Int {
+ if x.Cmp(y) < 0 {
+ return y
+ }
+ return x
+}
+
+// BigMin returns the smaller of x or y.
+func BigMin(x, y *big.Int) *big.Int {
+ if x.Cmp(y) > 0 {
+ return y
+ }
+ return x
+}
+
+// FirstBitSet returns the index of the first 1 bit in v, counting from LSB.
+func FirstBitSet(v *big.Int) int {
+ for i := 0; i < v.BitLen(); i++ {
+ if v.Bit(i) > 0 {
+ return i
+ }
+ }
+ return v.BitLen()
+}
+
+// PaddedBigBytes encodes a big integer as a big-endian byte slice. The length
+// of the slice is at least n bytes.
+func PaddedBigBytes(bigint *big.Int, n int) []byte {
+ if bigint.BitLen()/8 >= n {
+ return bigint.Bytes()
+ }
+ ret := make([]byte, n)
+ ReadBits(bigint, ret)
+ return ret
+}
+
+// bigEndianByteAt returns the byte at position n,
+// in Big-Endian encoding
+// So n==0 returns the least significant byte
+func bigEndianByteAt(bigint *big.Int, n int) byte {
+ words := bigint.Bits()
+ // Check word-bucket the byte will reside in
+ i := n / wordBytes
+ if i >= len(words) {
+ return byte(0)
+ }
+ word := words[i]
+ // Offset of the byte
+ shift := 8 * uint(n%wordBytes)
+
+ return byte(word >> shift)
+}
+
+// Byte returns the byte at position n,
+// with the supplied padlength in Little-Endian encoding.
+// n==0 returns the MSB
+// Example: bigint '5', padlength 32, n=31 => 5
+func Byte(bigint *big.Int, padlength, n int) byte {
+ if n >= padlength {
+ return byte(0)
+ }
+ return bigEndianByteAt(bigint, padlength-1-n)
+}
+
+// ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure
+// that buf has enough space. If buf is too short the result will be incomplete.
+func ReadBits(bigint *big.Int, buf []byte) {
+ i := len(buf)
+ for _, d := range bigint.Bits() {
+ for j := 0; j < wordBytes && i > 0; j++ {
+ i--
+ buf[i] = byte(d)
+ d >>= 8
+ }
+ }
+}
+
+// U256 encodes as a 256 bit two's complement number. This operation is destructive.
+func U256(x *big.Int) *big.Int {
+ return x.And(x, tt256m1)
+}
+
+// U256Bytes converts a big Int into a 256bit EVM number.
+// This operation is destructive.
+func U256Bytes(n *big.Int) []byte {
+ return PaddedBigBytes(U256(n), 32)
+}
+
+// S256 interprets x as a two's complement number.
+// x must not exceed 256 bits (the result is undefined if it does) and is not modified.
+//
+// S256(0) = 0
+// S256(1) = 1
+// S256(2**255) = -2**255
+// S256(2**256-1) = -1
+func S256(x *big.Int) *big.Int {
+ if x.Cmp(tt255) < 0 {
+ return x
+ }
+ return new(big.Int).Sub(x, tt256)
+}
+
+// Exp implements exponentiation by squaring.
+// Exp returns a newly-allocated big integer and does not change
+// base or exponent. The result is truncated to 256 bits.
+//
+// Courtesy @karalabe and @chfast
+func Exp(base, exponent *big.Int) *big.Int {
+ result := big.NewInt(1)
+
+ for _, word := range exponent.Bits() {
+ for i := 0; i < wordBits; i++ {
+ if word&1 == 1 {
+ U256(result.Mul(result, base))
+ }
+ U256(base.Mul(base, base))
+ word >>= 1
+ }
+ }
+ return result
+}
diff --git a/common/types.go b/common/types.go
new file mode 100644
index 0000000..fc6d9c3
--- /dev/null
+++ b/common/types.go
@@ -0,0 +1,428 @@
+// Copyright 2015 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 common
+
+import (
+ "bytes"
+ "database/sql/driver"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/big"
+ "math/rand"
+ "reflect"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/common/hexutil"
+ "golang.org/x/crypto/sha3"
+)
+
+// Lengths of hashes and addresses in bytes.
+const (
+ // HashLength is the expected length of the hash
+ HashLength = 32
+ // AddressLength is the expected length of the address
+ AddressLength = 20
+)
+
+var (
+ hashT = reflect.TypeOf(Hash{})
+ addressT = reflect.TypeOf(Address{})
+)
+
+// Hash represents the 32 byte Keccak256 hash of arbitrary data.
+type Hash [HashLength]byte
+
+// BytesToHash sets b to hash.
+// If b is larger than len(h), b will be cropped from the left.
+func BytesToHash(b []byte) Hash {
+ var h Hash
+ h.SetBytes(b)
+ return h
+}
+
+// BigToHash sets byte representation of b to hash.
+// If b is larger than len(h), b will be cropped from the left.
+func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
+
+// HexToHash sets byte representation of s to hash.
+// If b is larger than len(h), b will be cropped from the left.
+func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
+
+// Bytes gets the byte representation of the underlying hash.
+func (h Hash) Bytes() []byte { return h[:] }
+
+// Big converts a hash to a big integer.
+func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
+
+// Hex converts a hash to a hex string.
+func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
+
+// TerminalString implements log.TerminalStringer, formatting a string for console
+// output during logging.
+func (h Hash) TerminalString() string {
+ return fmt.Sprintf("%x..%x", h[:3], h[29:])
+}
+
+// String implements the stringer interface and is used also by the logger when
+// doing full logging into a file.
+func (h Hash) String() string {
+ return h.Hex()
+}
+
+// Format implements fmt.Formatter.
+// Hash supports the %v, %s, %q, %x, %X and %d format verbs.
+func (h Hash) Format(s fmt.State, c rune) {
+ hexb := make([]byte, 2+len(h)*2)
+ copy(hexb, "0x")
+ hex.Encode(hexb[2:], h[:])
+
+ switch c {
+ case 'x', 'X':
+ if !s.Flag('#') {
+ hexb = hexb[2:]
+ }
+ if c == 'X' {
+ hexb = bytes.ToUpper(hexb)
+ }
+ fallthrough
+ case 'v', 's':
+ s.Write(hexb)
+ case 'q':
+ q := []byte{'"'}
+ s.Write(q)
+ s.Write(hexb)
+ s.Write(q)
+ case 'd':
+ fmt.Fprint(s, ([len(h)]byte)(h))
+ default:
+ fmt.Fprintf(s, "%%!%c(hash=%x)", c, h)
+ }
+}
+
+// UnmarshalText parses a hash in hex syntax.
+func (h *Hash) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("Hash", input, h[:])
+}
+
+// UnmarshalJSON parses a hash in hex syntax.
+func (h *Hash) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(hashT, input, h[:])
+}
+
+// MarshalText returns the hex representation of h.
+func (h Hash) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(h[:]).MarshalText()
+}
+
+// SetBytes sets the hash to the value of b.
+// If b is larger than len(h), b will be cropped from the left.
+func (h *Hash) SetBytes(b []byte) {
+ if len(b) > len(h) {
+ b = b[len(b)-HashLength:]
+ }
+
+ copy(h[HashLength-len(b):], b)
+}
+
+// Generate implements testing/quick.Generator.
+func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
+ m := rand.Intn(len(h))
+ for i := len(h) - 1; i > m; i-- {
+ h[i] = byte(rand.Uint32())
+ }
+ return reflect.ValueOf(h)
+}
+
+// Scan implements Scanner for database/sql.
+func (h *Hash) Scan(src interface{}) error {
+ srcB, ok := src.([]byte)
+ if !ok {
+ return fmt.Errorf("can't scan %T into Hash", src)
+ }
+ if len(srcB) != HashLength {
+ return fmt.Errorf("can't scan []byte of len %d into Hash, want %d", len(srcB), HashLength)
+ }
+ copy(h[:], srcB)
+ return nil
+}
+
+// Value implements valuer for database/sql.
+func (h Hash) Value() (driver.Value, error) {
+ return h[:], nil
+}
+
+// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
+func (Hash) ImplementsGraphQLType(name string) bool { return name == "Bytes32" }
+
+// UnmarshalGraphQL unmarshals the provided GraphQL query data.
+func (h *Hash) UnmarshalGraphQL(input interface{}) error {
+ var err error
+ switch input := input.(type) {
+ case string:
+ err = h.UnmarshalText([]byte(input))
+ default:
+ err = fmt.Errorf("unexpected type %T for Hash", input)
+ }
+ return err
+}
+
+// UnprefixedHash allows marshaling a Hash without 0x prefix.
+type UnprefixedHash Hash
+
+// UnmarshalText decodes the hash from hex. The 0x prefix is optional.
+func (h *UnprefixedHash) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedUnprefixedText("UnprefixedHash", input, h[:])
+}
+
+// MarshalText encodes the hash as hex.
+func (h UnprefixedHash) MarshalText() ([]byte, error) {
+ return []byte(hex.EncodeToString(h[:])), nil
+}
+
+/////////// Address
+
+// Address represents the 20 byte address of an Ethereum account.
+type Address [AddressLength]byte
+
+// BytesToAddress returns Address with value b.
+// If b is larger than len(h), b will be cropped from the left.
+func BytesToAddress(b []byte) Address {
+ var a Address
+ a.SetBytes(b)
+ return a
+}
+
+// BigToAddress returns Address with byte values of b.
+// If b is larger than len(h), b will be cropped from the left.
+func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) }
+
+// HexToAddress returns Address with byte values of s.
+// If s is larger than len(h), s will be cropped from the left.
+func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
+
+// IsHexAddress verifies whether a string can represent a valid hex-encoded
+// Ethereum address or not.
+func IsHexAddress(s string) bool {
+ if has0xPrefix(s) {
+ s = s[2:]
+ }
+ return len(s) == 2*AddressLength && isHex(s)
+}
+
+// Bytes gets the string representation of the underlying address.
+func (a Address) Bytes() []byte { return a[:] }
+
+// Hash converts an address to a hash by left-padding it with zeros.
+func (a Address) Hash() Hash { return BytesToHash(a[:]) }
+
+// Hex returns an EIP55-compliant hex string representation of the address.
+func (a Address) Hex() string {
+ return string(a.checksumHex())
+}
+
+// String implements fmt.Stringer.
+func (a Address) String() string {
+ return a.Hex()
+}
+
+func (a *Address) checksumHex() []byte {
+ buf := a.hex()
+
+ // compute checksum
+ sha := sha3.NewLegacyKeccak256()
+ sha.Write(buf[2:])
+ hash := sha.Sum(nil)
+ for i := 2; i < len(buf); i++ {
+ hashByte := hash[(i-2)/2]
+ if i%2 == 0 {
+ hashByte = hashByte >> 4
+ } else {
+ hashByte &= 0xf
+ }
+ if buf[i] > '9' && hashByte > 7 {
+ buf[i] -= 32
+ }
+ }
+ return buf[:]
+}
+
+func (a Address) hex() []byte {
+ var buf [len(a)*2 + 2]byte
+ copy(buf[:2], "0x")
+ hex.Encode(buf[2:], a[:])
+ return buf[:]
+}
+
+// Format implements fmt.Formatter.
+// Address supports the %v, %s, %q, %x, %X and %d format verbs.
+func (a Address) Format(s fmt.State, c rune) {
+ switch c {
+ case 'v', 's':
+ s.Write(a.checksumHex())
+ case 'q':
+ q := []byte{'"'}
+ s.Write(q)
+ s.Write(a.checksumHex())
+ s.Write(q)
+ case 'x', 'X':
+ // %x disables the checksum.
+ hex := a.hex()
+ if !s.Flag('#') {
+ hex = hex[2:]
+ }
+ if c == 'X' {
+ hex = bytes.ToUpper(hex)
+ }
+ s.Write(hex)
+ case 'd':
+ fmt.Fprint(s, ([len(a)]byte)(a))
+ default:
+ fmt.Fprintf(s, "%%!%c(address=%x)", c, a)
+ }
+}
+
+// SetBytes sets the address to the value of b.
+// If b is larger than len(a), b will be cropped from the left.
+func (a *Address) SetBytes(b []byte) {
+ if len(b) > len(a) {
+ b = b[len(b)-AddressLength:]
+ }
+ copy(a[AddressLength-len(b):], b)
+}
+
+// MarshalText returns the hex representation of a.
+func (a Address) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(a[:]).MarshalText()
+}
+
+// UnmarshalText parses a hash in hex syntax.
+func (a *Address) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedText("Address", input, a[:])
+}
+
+// UnmarshalJSON parses a hash in hex syntax.
+func (a *Address) UnmarshalJSON(input []byte) error {
+ return hexutil.UnmarshalFixedJSON(addressT, input, a[:])
+}
+
+// Scan implements Scanner for database/sql.
+func (a *Address) Scan(src interface{}) error {
+ srcB, ok := src.([]byte)
+ if !ok {
+ return fmt.Errorf("can't scan %T into Address", src)
+ }
+ if len(srcB) != AddressLength {
+ return fmt.Errorf("can't scan []byte of len %d into Address, want %d", len(srcB), AddressLength)
+ }
+ copy(a[:], srcB)
+ return nil
+}
+
+// Value implements valuer for database/sql.
+func (a Address) Value() (driver.Value, error) {
+ return a[:], nil
+}
+
+// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
+func (a Address) ImplementsGraphQLType(name string) bool { return name == "Address" }
+
+// UnmarshalGraphQL unmarshals the provided GraphQL query data.
+func (a *Address) UnmarshalGraphQL(input interface{}) error {
+ var err error
+ switch input := input.(type) {
+ case string:
+ err = a.UnmarshalText([]byte(input))
+ default:
+ err = fmt.Errorf("unexpected type %T for Address", input)
+ }
+ return err
+}
+
+// UnprefixedAddress allows marshaling an Address without 0x prefix.
+type UnprefixedAddress Address
+
+// UnmarshalText decodes the address from hex. The 0x prefix is optional.
+func (a *UnprefixedAddress) UnmarshalText(input []byte) error {
+ return hexutil.UnmarshalFixedUnprefixedText("UnprefixedAddress", input, a[:])
+}
+
+// MarshalText encodes the address as hex.
+func (a UnprefixedAddress) MarshalText() ([]byte, error) {
+ return []byte(hex.EncodeToString(a[:])), nil
+}
+
+// MixedcaseAddress retains the original string, which may or may not be
+// correctly checksummed
+type MixedcaseAddress struct {
+ addr Address
+ original string
+}
+
+// NewMixedcaseAddress constructor (mainly for testing)
+func NewMixedcaseAddress(addr Address) MixedcaseAddress {
+ return MixedcaseAddress{addr: addr, original: addr.Hex()}
+}
+
+// NewMixedcaseAddressFromString is mainly meant for unit-testing
+func NewMixedcaseAddressFromString(hexaddr string) (*MixedcaseAddress, error) {
+ if !IsHexAddress(hexaddr) {
+ return nil, errors.New("invalid address")
+ }
+ a := FromHex(hexaddr)
+ return &MixedcaseAddress{addr: BytesToAddress(a), original: hexaddr}, nil
+}
+
+// UnmarshalJSON parses MixedcaseAddress
+func (ma *MixedcaseAddress) UnmarshalJSON(input []byte) error {
+ if err := hexutil.UnmarshalFixedJSON(addressT, input, ma.addr[:]); err != nil {
+ return err
+ }
+ return json.Unmarshal(input, &ma.original)
+}
+
+// MarshalJSON marshals the original value
+func (ma *MixedcaseAddress) MarshalJSON() ([]byte, error) {
+ if strings.HasPrefix(ma.original, "0x") || strings.HasPrefix(ma.original, "0X") {
+ return json.Marshal(fmt.Sprintf("0x%s", ma.original[2:]))
+ }
+ return json.Marshal(fmt.Sprintf("0x%s", ma.original))
+}
+
+// Address returns the address
+func (ma *MixedcaseAddress) Address() Address {
+ return ma.addr
+}
+
+// String implements fmt.Stringer
+func (ma *MixedcaseAddress) String() string {
+ if ma.ValidChecksum() {
+ return fmt.Sprintf("%s [chksum ok]", ma.original)
+ }
+ return fmt.Sprintf("%s [chksum INVALID]", ma.original)
+}
+
+// ValidChecksum returns true if the address has valid checksum
+func (ma *MixedcaseAddress) ValidChecksum() bool {
+ return ma.original == ma.addr.Hex()
+}
+
+// Original returns the mixed-case input string
+func (ma *MixedcaseAddress) Original() string {
+ return ma.original
+}
diff --git a/core/chainsql.go b/core/chainsql.go
index 34a4aa3..f5766d7 100644
--- a/core/chainsql.go
+++ b/core/chainsql.go
@@ -1,19 +1,32 @@
package core
import (
- "log"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "strings"
"github.com/ChainSQL/go-chainsql-api/crypto"
. "github.com/ChainSQL/go-chainsql-api/data"
"github.com/ChainSQL/go-chainsql-api/export"
"github.com/ChainSQL/go-chainsql-api/net"
"github.com/ChainSQL/go-chainsql-api/util"
+ "github.com/buger/jsonparser"
)
// Chainsql is the interface struct for this package
type Chainsql struct {
- client *net.Client
+ // client *net.Client
SubmitBase
+ op *ChainsqlTxInfo
+}
+
+//TxInfo is the opearting details
+type ChainsqlTxInfo struct {
+ //Signer Signer
+ Raw string
+ TxType TransactionType
+ Query []interface{}
}
type TableGetSqlJSON struct {
@@ -25,9 +38,12 @@ type TableGetSqlJSON struct {
// NewChainsql is a function that create a chainsql object
func NewChainsql() *Chainsql {
chainsql := &Chainsql{
- client: net.NewClient(),
+ // client: net.NewClient(),
+ op: &ChainsqlTxInfo{
+ Query: make([]interface{}, 0),
+ },
}
- chainsql.SubmitBase.client = chainsql.client
+ chainsql.client = net.NewClient()
chainsql.SubmitBase.IPrepare = chainsql
return chainsql
}
@@ -47,22 +63,14 @@ func (c *Chainsql) Use(owner string) {
c.client.Auth.Owner = owner
}
-// PrepareTx prepare tx json for submit
-func (c *Chainsql) PrepareTx() (Signer, error) {
-
- log.Println("Chainsql prepareTx")
- tx := &TableListSet{}
- return tx, nil
-}
-
//Table create a new table object
func (c *Chainsql) Table(name string) *Table {
return NewTable(name, c.client)
}
//Connect is used to create a websocket connection
-func (c *Chainsql) Connect(url string) error {
- return c.client.Connect(url)
+func (c *Chainsql) Connect(url, tlsRootCertPath, tlsClientCertPath, tlsClientKeyPath, serverName string) error {
+ return c.client.Connect(url, tlsRootCertPath, tlsClientCertPath, tlsClientKeyPath, serverName)
}
// GetLedger request a ledger
@@ -70,6 +78,11 @@ func (c *Chainsql) GetLedger(seq int) string {
return c.client.GetLedger(seq)
}
+// GetLedgerTransactions request a ledger
+func (c *Chainsql) GetLedgerTransactions(seq int, expand bool) string {
+ return c.client.GetLedgerTransactions(seq, expand)
+}
+
//OnLedgerClosed reponses in callback functor
func (c *Chainsql) OnLedgerClosed(callback export.Callback) {
c.client.Event.SubscribeLedger(callback)
@@ -82,12 +95,16 @@ func (c *Chainsql) OnLedgerClosed(callback export.Callback) {
// "publicKeyHex":"02EA30B2A25844D4AFBAF6020DA9C9FED573AA0058791BFC8642E69888693CF8EA",
// "privateKey":"xniMQKhxZTMbfWb8scjRPXa5Zv6HB",
// }
-func (c *Chainsql) GenerateAccount(args ...string) (string, error) {
+/*func (c *Chainsql) GenerateAccount(args ...string) (string, error) {
if len(args) == 0 {
return crypto.GenerateAccount()
} else {
return crypto.GenerateAccount(args[0])
}
+}*/
+
+func (c *Chainsql) GenerateAddress(options string) (string, error) {
+ return crypto.GenerateAddress(options)
}
//SignPlainData sign a plain text and return the signature
@@ -109,7 +126,7 @@ func (c *Chainsql) GetBySqlUser(sql string) (string, error) {
if c.client.ServerInfo.Updated {
data.LedgerIndex = c.client.ServerInfo.LedgerIndex
} else {
- ledgerIndex, err := c.client.GetLedgerVersion()
+ ledgerIndex, err := c.client.GetLedgerCurrent()
if err != nil {
return "", err
}
@@ -127,6 +144,7 @@ func (c *Chainsql) IsConnected() bool {
func (c *Chainsql) Disconnect() {
if c.client.GetWebocketManager() != nil {
+ c.client.Unsubscribe()
c.client.GetWebocketManager().Disconnect()
}
}
@@ -136,32 +154,270 @@ func (c *Chainsql) ValidationCreate() (string, error) {
}
func (c *Chainsql) GetServerInfo() (string, error) {
- return "", nil
+ return c.client.GetServerInfo()
}
func (c *Chainsql) GetAccountInfo(address string) (string, error) {
- return crypto.GetAccountInfo(address)
+ return c.client.GetAccountInfo(address)
}
-func (c *Chainsql) Pay(accountId string, value string) *Ripple {
- r := NewRipple()
+func (c *Chainsql) Pay(accountId string, value int64) *Ripple {
+ r := NewRipple(c.client)
return r.Pay(accountId, value)
}
func (c *Chainsql) CreateSchema(schemaInfo string) *Chainsql {
+ c.op.TxType = SCHEMA_CREATE
+ c.op.Raw = schemaInfo
+ return c
+}
+
+func (c *Chainsql) createSchema() (Signer, error) {
+ var schemaInfo = c.op.Raw
+ isValid := strings.Contains(schemaInfo, "SchemaName") && strings.Contains(schemaInfo, "WithState") &&
+ strings.Contains(schemaInfo, "Validators") && strings.Contains(schemaInfo, "PeerList")
+
+ if !isValid {
+ return nil, fmt.Errorf("Invalid schemaInfo parameter")
+ }
+ createSchema := &SchemaCreate{TxBase: TxBase{TransactionType: SCHEMA_CREATE}}
+ var jsonObj CreateSchema
+ err := json.Unmarshal([]byte(schemaInfo), &jsonObj)
+ if err != nil {
+ return nil, err
+ }
+
+ createSchema.SchemaName = VariableLength(jsonObj.SchemaName)
+ if strings.Contains(schemaInfo, "SchemaAdmin") {
+ account, err := NewAccountFromAddress(jsonObj.SchemaAdmin)
+ if err != nil {
+ return nil, fmt.Errorf("Invalid schemaInfo parameter: SchemaAdmin")
+ }
+ if account != nil {
+ createSchema.SchemaAdmin = account
+ }
+ }
+
+ if jsonObj.WithState {
+ //继承主链的节点状态
+ if strings.Contains(schemaInfo, "AnchorLedgerHash") {
+ leadgerHash, errHash := NewHash256(jsonObj.AnchorLedgerHash)
+ if errHash != nil {
+ return nil, fmt.Errorf("Invalid schemaInfo parameter: AnchorLedgerHash")
+ }
+ if leadgerHash != nil {
+ createSchema.AnchorLedgerHash = leadgerHash
+ }
+ }
+ createSchema.SchemaStrategy = 2
+ } else {
+ // 不继承主链的节点状态
+ createSchema.SchemaStrategy = 1
+ if strings.Contains(schemaInfo, "AnchorLedgerHash") {
+ return nil, fmt.Errorf("Field 'AnchorLedgerHash' is unnecessary")
+ }
+ }
+ validatorSlice := make([]ValidatorFormat, len(jsonObj.Validators))
+ for i := 0; i < len(jsonObj.Validators); i++ {
+ publicKeyHex := jsonObj.Validators[i].Validator.PublicKey
+ publicKey, _ := hex.DecodeString(publicKeyHex)
+ validatorSlice[i].Validator.PublicKey = VariableLength(publicKey)
+ }
+ createSchema.Validators = validatorSlice
+ peerSlice := make([]PeerFormat, len(jsonObj.PeerList))
+ for i := 0; i < len(jsonObj.PeerList); i++ {
+ endpoint := jsonObj.PeerList[i].Peer.Endpoint
+ peerSlice[i].Peer.Endpoint = VariableLength(endpoint)
+ }
+
+ createSchema.PeerList = peerSlice
+ var signer Signer = createSchema
+ return signer, nil
+}
+func (c *Chainsql) ModifySchema(schemaType string, schemaInfo string) *Chainsql {
+ c.op.TxType = SCHEMA_MODIFY
+ c.op.Raw = "{\"SchemaType\": \"" + schemaType + "\", \"SchemaInfo\":" + schemaInfo + "}"
return c
}
-func (c *Chainsql) ModifySchema(schemaInfo string) *Chainsql {
+func (c *Chainsql) modifySchema() (Signer, error) {
+ schemaType, _ := jsonparser.GetString([]byte(c.op.Raw), "SchemaType")
+ result, _, _, _ := jsonparser.Get([]byte(c.op.Raw), "SchemaInfo")
+ schemaInfo := string(result)
+ isValid := strings.Contains(schemaInfo, "SchemaID") && strings.Contains(schemaInfo, "Validators") && strings.Contains(schemaInfo, "PeerList")
+
+ if !isValid {
+ return nil, fmt.Errorf("Invalid schemaInfo parameter")
+ }
+ var jsonObj ModifySchema
+ errUnmarshal := json.Unmarshal([]byte(schemaInfo), &jsonObj)
+ if errUnmarshal != nil {
+ return nil, errUnmarshal
+ }
+ schemaModify := &SchemaModify{TxBase: TxBase{TransactionType: SCHEMA_MODIFY}}
+ if schemaType == util.SchemaDel {
+ schemaModify.OpType = util.OpTypeSchemaDel
+ } else {
+ schemaModify.OpType = util.OpTypeSchemaAdd
+ }
+ validatorSlice := make([]ValidatorFormat, len(jsonObj.Validators))
+ for i := 0; i < len(jsonObj.Validators); i++ {
+ publicKeyHex := jsonObj.Validators[i].Validator.PublicKey
+ publicKey, _ := hex.DecodeString(publicKeyHex)
+ validatorSlice[i].Validator.PublicKey = VariableLength(publicKey)
+ }
+ schemaModify.Validators = validatorSlice
+
+ peerSlice := make([]PeerFormat, len(jsonObj.PeerList))
+ for i := 0; i < len(jsonObj.PeerList); i++ {
+ endpoint := jsonObj.PeerList[i].Peer.Endpoint
+ peerSlice[i].Peer.Endpoint = VariableLength(endpoint)
+ }
+ schemaModify.PeerList = peerSlice
+ schemaIdHash, errHash := NewHash256(jsonObj.SchemaID)
+ if errHash != nil {
+ return nil, errHash
+ }
+ if schemaIdHash != nil {
+ schemaModify.SchemaID = *schemaIdHash
+ }
+ //schemaModify.TransactionType = SCHEMA_MODIFY
+ var signer Signer = schemaModify
+ return signer, nil
+}
+func (c *Chainsql) DeleteSchema(schemaID string) *Chainsql {
+ c.op.TxType = SCHEMA_DELETE
+ c.op.Raw = schemaID
return c
}
+func (c *Chainsql) deleteSchema() (Signer, error) {
+ var schemaID = c.op.Raw
+ if schemaID == "" {
+ return nil, fmt.Errorf("Invalid parameter")
+ }
+ schemaDelete := &SchemaDelete{TxBase: TxBase{TransactionType: SCHEMA_DELETE}}
+ schemaIdHash, errHash := NewHash256(schemaID)
+ if errHash != nil {
+ return nil, errHash
+ }
+ if schemaIdHash != nil {
+ schemaDelete.SchemaID = *schemaIdHash
+ }
+ var signer Signer = schemaDelete
+ return signer, nil
+}
+
func (c *Chainsql) GetSchemaList(params string) (string, error) {
- return "", nil
+ return c.client.GetSchemaList(params)
}
-func (c *Chainsql) UpdateSchemaConfig(params string) *Chainsql {
- return c
+func (c *Chainsql) GetSchemaInfo(schemaID string) (string, error) {
+ return c.client.GetSchemaInfo(schemaID)
+}
+
+func (c *Chainsql) StopSchema(schemaID string) (string, error) {
+ return c.client.StopSchema(schemaID)
+}
+
+func (c *Chainsql) StartSchema(schemaID string) (string, error) {
+ return c.client.StartSchema(schemaID)
+}
+
+func (c *Chainsql) SetSchema(schemaId string) {
+ if c.client.SchemaID != schemaId {
+ c.client.Unsubscribe()
+ c.client.SchemaID = schemaId
+ c.client.InitSubscription()
+ }
+}
+func (c *Chainsql) GetSchemaId(hash string) (string, error) {
+ response, _ := c.client.GetTransaction(hash)
+ if response == "" {
+ return "", fmt.Errorf("Transaction does not exist ")
+ }
+ schemaID := ""
+ flag := false
+ //LedgerEntryType, err := jsonparser.GetString([]byte(response), "result", "meta", "AffectedNodes", "[0]", "CreatedNode", "LedgerEntryType")
+ jsonparser.ArrayEach([]byte(response), func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
+ LedgerEntryType, err := jsonparser.GetString(value, "CreatedNode", "LedgerEntryType")
+ if err == nil {
+ if LedgerEntryType == "Schema" {
+ schemaID, _ = jsonparser.GetString([]byte(value), "CreatedNode", "LedgerIndex")
+ flag = true
+ }
+ }
+
+ }, "result", "meta", "AffectedNodes")
+ if flag {
+ return schemaID, nil
+ }
+ return "", fmt.Errorf("Invalid parameter")
+}
+func (c *Chainsql) GetTransaction(hash string) (string, error) {
+ return c.client.GetTransaction(hash)
+}
+
+func (c *Chainsql) GetTransactionResult(hash string) (string, error) {
+ return c.client.GetTransactionResult(hash)
+}
+
+// PrepareTx prepare tx json for submit
+func (c *Chainsql) PrepareTx() (Signer, error) {
+ var tx Signer
+ var err error
+ switch c.op.TxType {
+ case SCHEMA_CREATE:
+ tx, err = c.createSchema()
+ break
+ case SCHEMA_MODIFY:
+ tx, err = c.modifySchema()
+ break
+ case SCHEMA_DELETE:
+ tx, err = c.deleteSchema()
+ break
+ default:
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ return c.prepareTxBase(tx)
+}
+
+func (c *Chainsql) prepareTxBase(tx Signer) (Signer, error) {
+
+ //tx := c.op.Signer
+ seq, err := net.PrepareRipple(c.client)
+ if err != nil {
+ return nil, err
+ }
+
+ fee, lastLedgerSeq, err := net.PrepareLastLedgerSeqAndFee(c.client)
+ if err != nil {
+ return nil, err
+ }
+
+ if tx.GetRaw() != "" {
+ fee += util.GetExtraFee(tx.GetRaw(), c.client.ServerInfo.DropsPerByte)
+ } else if tx.GetStatements() != "" {
+ fee += util.GetExtraFee(tx.GetStatements(), c.client.ServerInfo.DropsPerByte)
+ }
+
+ finalFee, err := NewNativeValue(fee)
+ if err != nil {
+ return nil, err
+ }
+ account, err := NewAccountFromAddress(c.client.Auth.Address)
+ if err != nil {
+ return nil, err
+ }
+ tx.SetTxBase(seq, *finalFee, &lastLedgerSeq, *account)
+ return tx, nil
+}
+
+func (c *Chainsql) GetLedgerVersion() (int, error) {
+ return c.client.GetLedgerVersion()
}
diff --git a/core/contract.go b/core/contract.go
new file mode 100644
index 0000000..6f5fe2a
--- /dev/null
+++ b/core/contract.go
@@ -0,0 +1,625 @@
+// Copyright 2015 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 core
+
+import (
+ "context"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "log"
+ "regexp"
+ "strings"
+ "sync"
+
+ "github.com/ChainSQL/go-chainsql-api/abigen/abi"
+ . "github.com/ChainSQL/go-chainsql-api/abigen/abi/bind"
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/crypto"
+ "github.com/ChainSQL/go-chainsql-api/data"
+ . "github.com/ChainSQL/go-chainsql-api/data"
+ "github.com/ChainSQL/go-chainsql-api/export"
+ "github.com/ChainSQL/go-chainsql-api/net"
+ "github.com/ChainSQL/go-chainsql-api/util"
+ "github.com/buger/jsonparser"
+)
+
+// SignerFn is a signer function callback when a contract requires a method to
+// sign the transaction before submission.
+// type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)
+
+// CallOpts is the collection of options to fine tune a contract call request.
+type CallOpts struct {
+ LedgerIndex int64 // Optional the block number on which the call should be performed
+}
+
+type CallReq struct {
+ common.RequestBase
+ Account string `json:"account"`
+ ContractAddress string `json:"contract_address"`
+ ContractData string `json:"contract_data"`
+ LedgerIndex uint32 `json:"ledger_index"`
+}
+
+// TransactOpts is the collection of authorization data required to create a
+// valid ChainSQL transaction.
+type TransactOpts struct {
+ ContractValue int64 // Funds to transfer along the transaction (nil = 0 = no funds)
+ Gas uint32
+ Expectation string
+}
+
+type DeployTxRet struct {
+ common.TxResult
+ ContractAddress string `json:"contractAddress"`
+}
+
+// FilterOpts is the collection of options to fine tune filtering for events
+// within a bound contract.
+type FilterOpts struct {
+ Start uint64 // Start of the queried range
+ End *uint64 // End of the range (nil = latest)
+
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// WatchOpts is the collection of options to fine tune subscribing for events
+// within a bound contract.
+type WatchOpts struct {
+ Start *uint64 // Start of the queried range (nil = latest)
+ Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
+}
+
+// CtrMetaData collects all metadata for a bound contract.
+type CtrMetaData struct {
+ mu sync.Mutex
+ Sigs map[string]string
+ Bin string
+ ABI string
+ ab *abi.ABI
+}
+
+func (m *CtrMetaData) GetAbi() (*abi.ABI, error) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.ab != nil {
+ return m.ab, nil
+ }
+ if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
+ return nil, err
+ } else {
+ m.ab = &parsed
+ }
+ return m.ab, nil
+}
+
+// BoundContract is the base wrapper object that reflects a contract on the
+// Ethereum network. It contains a collection of methods that are used by the
+// higher level contract bindings to operate.
+type BoundContract struct {
+ SubmitBase
+ address string // Deployment address of the contract on the ChainSQL blockchain
+ abi abi.ABI // Reflect based ABI to access the correct ChainSQL methods
+ caller ContractCaller // Read interface to interact with the blockchain
+ transactor ContractTransactor // Write interface to interact with the blockchain
+ filterer ContractFilterer // Event filtering to interact with the blockchain
+ TransactOpts
+ ContractOpType uint16
+ ContractData []byte
+ isFirstSubscribe bool
+ ctrEventCache map[string]export.Callback
+}
+
+// NewBoundContract creates a low level contract interface through which calls
+// and transactions may be made through.
+// func NewBoundContract(chainsql *Chainsql, address string, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
+func NewBoundContract(chainsql *Chainsql, address string, abi abi.ABI) *BoundContract {
+ // bCtr := &BoundContract{
+ // address: address,
+ // abi: abi,
+ // caller: caller,
+ // transactor: transactor,
+ // filterer: filterer,
+ // }
+ bCtr := &BoundContract{
+ address: address,
+ abi: abi,
+ ContractOpType: 2,
+ isFirstSubscribe: true,
+ }
+ bCtr.client = chainsql.client
+ bCtr.IPrepare = bCtr
+ return bCtr
+}
+
+// DeployContract deploys a contract onto the ChainSQL blockchain and binds the
+// deployment address with a Go wrapper.
+// func DeployContract(chainsql *Chainsql, opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (*DeployTxRet, *BoundContract, error) {
+func DeployContract(chainsql *Chainsql, opts *TransactOpts, abi abi.ABI, bytecode []byte, params ...interface{}) (*DeployTxRet, *BoundContract, error) {
+ // Otherwise try to deploy the contract
+ // c := NewBoundContract(chainsql, "", abi, backend, backend, backend)
+ c := NewBoundContract(chainsql, "", abi)
+ c.ContractOpType = 1
+
+ input, err := c.abi.Pack("", params...)
+ if err != nil {
+ return nil, nil, err
+ }
+ if opts.Expectation == "" {
+ opts.Expectation = util.ValidateSuccess
+ } else if opts.Expectation == util.SendSuccess {
+ return nil, nil, errors.New("contract deploy tx expect must be validate_success or db_success")
+ }
+ txRet, err := c.transact(opts, append(bytecode, input...))
+ if err != nil {
+ return nil, nil, err
+ }
+
+ deployTxRet := &DeployTxRet{}
+ deployTxRet.Status = txRet.Status
+ deployTxRet.TxHash = txRet.TxHash
+
+ ret, err := c.client.GetTransaction(deployTxRet.TxHash)
+ if err == nil {
+ txAddr, err := jsonparser.GetString([]byte(ret), "result", "Account")
+ if err != nil {
+ return deployTxRet, c, err
+ }
+ txAddrSeq, err := jsonparser.GetInt([]byte(ret), "result", "Sequence")
+ if err != nil {
+ return deployTxRet, c, err
+ }
+ txRetMeta, _, _, err := jsonparser.Get([]byte(ret), "result", "meta", "AffectedNodes")
+ if err != nil {
+ return deployTxRet, c, err
+ }
+
+ var txDetailCtrAddr string
+ _, _ = jsonparser.ArrayEach(txRetMeta, func(value []byte, dataType jsonparser.ValueType, offset int, errin error) {
+ ctrAddr, err := jsonparser.GetString(value, "CreatedNode", "NewFields", "Account")
+ if err != nil {
+ return
+ }
+ txDetailCtrAddr = ctrAddr
+ // return
+ })
+ c.address, err = crypto.CreateContractAddr(txAddr, uint32(txAddrSeq))
+ if err != nil {
+ deployTxRet.ErrorMessage = err.Error()
+ return deployTxRet, c, err
+ }
+ if txDetailCtrAddr == "" || c.address != txDetailCtrAddr {
+ err := errors.New("mismatch, can't find correct contract address")
+ deployTxRet.ErrorMessage = err.Error()
+ return deployTxRet, c, err
+ }
+ deployTxRet.ContractAddress = c.address
+ } else {
+ err := errors.New("Get Tx detail failed, can't find correct contract address")
+ deployTxRet.ErrorMessage = err.Error()
+ return deployTxRet, c, err
+ }
+
+ return deployTxRet, c, nil
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(CallOpts)
+ }
+ if results == nil {
+ results = new([]interface{})
+ }
+ // Pack the input, call and unpack the results
+ input, err := c.abi.Pack(method, params...)
+ if err != nil {
+ return err
+ }
+ inputHexStr := fmt.Sprintf("%x", input)
+ callReq := &CallReq{
+ Account: c.client.Auth.Address,
+ ContractAddress: c.address,
+ ContractData: inputHexStr,
+ }
+ if opts.LedgerIndex == 0 {
+ _, lastLedgerSeq, _ := net.PrepareLastLedgerSeqAndFee(c.client)
+
+ callReq.LedgerIndex = lastLedgerSeq - 20
+ }
+
+ callReq.Command = "contract_call"
+ request := c.client.SyncRequest(callReq)
+
+ err = c.client.ParseResponseError(request)
+ if err != nil {
+ return err
+ }
+
+ contractCallRet, err := jsonparser.GetString([]byte(request.Response.Value), "result", "contract_call_result")
+ if err != nil {
+ return err
+ }
+ ctrCallRetHex, err := hex.DecodeString(contractCallRet[2:])
+ if err != nil {
+ return err
+ }
+ if len(*results) == 0 {
+ res, err := c.abi.Unpack(method, ctrCallRetHex)
+ *results = res
+ return err
+ }
+ res := *results
+ return c.abi.UnpackIntoInterface(res[0], method, ctrCallRetHex)
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+// func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+func (c *BoundContract) Transact(opts *TransactOpts, method string, params ...interface{}) (*common.TxResult, error) {
+ // Otherwise pack up the parameters and invoke the contract
+ input, err := c.abi.Pack(method, params...)
+ if err != nil {
+ return nil, err
+ }
+ // todo(rjl493456442) check the method is payable or not,
+ // reject invalid transaction at the first place
+ // return c.transact(opts, &c.address, input)
+ return c.transact(opts, input)
+}
+
+// // RawTransact initiates a transaction with the given raw calldata as the input.
+// // It's usually used to initiate transactions for invoking **Fallback** function.
+// func (c *BoundContract) RawTransact(opts *TransactOpts, calldata []byte) (*types.Transaction, error) {
+// // todo(rjl493456442) check the method is payable or not,
+// // reject invalid transaction at the first place
+// return c.transact(opts, &c.address, calldata)
+// }
+
+// // Transfer initiates a plain transaction to move funds to the contract, calling
+// // its default method if one is available.
+// func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error) {
+// // todo(rjl493456442) check the payable fallback or receive is defined
+// // or not, reject invalid transaction at the first place
+// return c.transact(opts, &c.address, nil)
+// }
+
+// func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Address, input []byte, gasPrice, gasTipCap, gasFeeCap, value *big.Int) (uint64, error) {
+// if contract != nil {
+// // Gas estimation cannot succeed without code for method invocations.
+// if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
+// return 0, err
+// } else if len(code) == 0 {
+// return 0, ErrNoCode
+// }
+// }
+// msg := ethereum.CallMsg{
+// From: opts.From,
+// To: contract,
+// GasPrice: gasPrice,
+// GasTipCap: gasTipCap,
+// GasFeeCap: gasFeeCap,
+// Value: value,
+// Data: input,
+// }
+// return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
+// }
+
+// func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
+// if opts.Nonce == nil {
+// return c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
+// } else {
+// return opts.Nonce.Uint64(), nil
+// }
+// }
+
+func (c *BoundContract) PrepareTx() (Signer, error) {
+ contractTxObj := &ContractTx{}
+
+ contractTxObj.TransactionType = CONTRACT
+ account, err := NewAccountFromAddress(c.client.Auth.Address)
+ if err != nil {
+ return nil, err
+ }
+ contractTxObj.Account = *account
+ seq, err := net.PrepareRipple(c.client)
+ if err != nil {
+ return nil, err
+ }
+ contractTxObj.Sequence = seq
+ contractTxObj.ContractOpType = c.ContractOpType
+
+ // if contractTxObj.ContractOpType == 1 {
+ // var zeroAccount Account
+ // contractTxObj.ContractAddress = zeroAccount
+ // } else {
+ if contractTxObj.ContractOpType == 2 {
+ contracAddress, err := NewAccountFromAddress(c.address)
+ if err != nil {
+ return nil, err
+ }
+ contractTxObj.ContractAddress = *contracAddress
+ }
+
+ contractTxObj.ContractData = c.ContractData
+
+ contractValue, _ := NewNativeValue(c.ContractValue)
+ currencyZxc, _ := NewCurrency("ZXC")
+ contractAmount := Amount{
+ Value: contractValue,
+ Currency: currencyZxc,
+ }
+ contractTxObj.ContractValue = contractAmount
+ contractTxObj.Gas = c.Gas
+
+ fee, lastLedgerSeq, err := net.PrepareLastLedgerSeqAndFee(c.client)
+ if err != nil {
+ return nil, err
+ }
+
+ contractTxObj.LastLedgerSequence = &lastLedgerSeq
+ finalFee, err := NewNativeValue(fee)
+ if err != nil {
+ return nil, err
+ }
+ contractTxObj.Fee = *finalFee
+
+ return contractTxObj, nil
+}
+
+// transact executes an actual transaction invocation, first deriving any missing
+// authorization fields, and then scheduling the transaction for execution.
+// func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*common.TxResult, error) {
+func (c *BoundContract) transact(opts *TransactOpts, input []byte) (*common.TxResult, error) {
+ // Create the transaction
+ c.ContractValue = opts.ContractValue
+ c.Gas = opts.Gas
+ c.ContractData = input
+
+ return c.Submit(opts.Expectation), nil
+}
+
+// FilterLogs filters contract logs for past blocks, returning the necessary
+// channels to construct a strongly typed bound iterator on top of them.
+// func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
+// // Don't crash on a lazy user
+// if opts == nil {
+// opts = new(FilterOpts)
+// }
+// // Append the event selector to the query parameters and construct the topic set
+// query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
+
+// topics, err := abi.MakeTopics(query...)
+// if err != nil {
+// return nil, nil, err
+// }
+// // Start the background filtering
+// logs := make(chan types.Log, 128)
+
+// config := ethereum.FilterQuery{
+// Addresses: []common.Address{c.address},
+// Topics: topics,
+// FromBlock: new(big.Int).SetUint64(opts.Start),
+// }
+// if opts.End != nil {
+// config.ToBlock = new(big.Int).SetUint64(*opts.End)
+// }
+// /* TODO(karalabe): Replace the rest of the method below with this when supported
+// sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
+// */
+// buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config)
+// if err != nil {
+// return nil, nil, err
+// }
+// sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
+// for _, log := range buff {
+// select {
+// case logs <- log:
+// case <-quit:
+// return nil
+// }
+// }
+// return nil
+// }), nil
+
+// if err != nil {
+// return nil, nil, err
+// }
+// return logs, sub, nil
+// }
+type EventSub struct {
+ eventSign string
+ EventMsgCh chan *data.Log
+ err chan error
+ ctr *BoundContract
+}
+
+func (e *EventSub) UnSubscribe() {
+ e.ctr.client.UnRegisterCtrEvent(e.eventSign, e.EventMsgCh)
+}
+
+func (e *EventSub) Err() chan error {
+ return e.err
+}
+
+// WatchLogs filters subscribes to contract logs for future blocks, returning a
+// subscription object that can be used to tear down the watcher.
+// func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan data.Log, event.Subscription, error) {
+func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (*EventSub, error) {
+ // Don't crash on a lazy user
+ if opts == nil {
+ opts = new(WatchOpts)
+ }
+ // Append the event selector to the query parameters and construct the topic set
+ query = append([][]interface{}{{c.abi.Events[name].ID}}, query...)
+
+ topics, err := abi.MakeTopics(query...)
+ if err != nil {
+ return nil, err
+ }
+ log.Println(topics[0][0])
+
+ // Start the background filtering
+ // logs := make(chan data.Log, 128)
+
+ if c.isFirstSubscribe {
+ c.client.SubscribeCtrAddr(c.address, true)
+ c.isFirstSubscribe = false
+ }
+
+ eventMsgCh := make(chan *data.Log)
+ errCh := make(chan error)
+ eventSig := topics[0][0].String()
+ c.client.RegisterCtrEvent(eventSig, eventMsgCh)
+
+ sub := &EventSub{
+ eventSign: eventSig,
+ EventMsgCh: eventMsgCh,
+ ctr: c,
+ err: errCh,
+ }
+
+ return sub, nil
+}
+
+func (c *BoundContract) GetPastEventByCtrLog(ContractLogs string) ([]*data.Log, error) {
+ contractLogsHexBytes, _ := hex.DecodeString(ContractLogs)
+ contractLogsStr := string(contractLogsHexBytes)
+
+ reg1 := regexp.MustCompile(`^\[|\n|\s+|]$`)
+ contractLogsStr1 := reg1.ReplaceAllString(contractLogsStr, "")
+ // log.Println(contractLogsStr1)
+ reg2 := regexp.MustCompile(`,{/`)
+ contractLogsStr2 := reg2.ReplaceAllString(contractLogsStr1, "-{")
+
+ ctrLogsArray := strings.Split(contractLogsStr2, `-`)
+
+ var newCtrLogs []*data.Log
+ for _, str := range ctrLogsArray {
+ logRaw := &data.Log{
+ Address: c.address,
+ }
+
+ contractData, err := jsonparser.GetString([]byte(str), "contract_data")
+ if err != nil {
+ return nil, err
+ }
+ ctrEventDataHex, err := hex.DecodeString(contractData)
+ if err != nil {
+ return nil, err
+ }
+ logRaw.Data = ctrEventDataHex
+ contractTopics, _, _, err := jsonparser.Get([]byte(str), "contract_topics")
+ if err != nil {
+ return nil, err
+ }
+ _, _ = jsonparser.ArrayEach(contractTopics, func(value []byte, dataType jsonparser.ValueType, offset int, errin error) {
+ valueStr := string(value)
+ valueHex, err := hex.DecodeString(valueStr)
+ if err != nil {
+ return
+ }
+ logRaw.Topics = append(logRaw.Topics, common.BytesToHash(valueHex))
+ })
+ newCtrLogs = append(newCtrLogs, logRaw)
+ }
+
+ return newCtrLogs, nil
+}
+
+func (c *BoundContract) GetPastEventByTxHash(txHash string) ([]*data.Log, error) {
+ if txHash == "" {
+ return nil, errors.New("txHash is empty")
+ }
+
+ txDetailStr, err := c.client.GetTransaction(txHash)
+ if err != nil {
+ return nil, err
+ }
+ // log.Println(txDetailStr)
+
+ txType, err := jsonparser.GetString([]byte(txDetailStr), "result", "TransactionType")
+ if err != nil {
+ return nil, err
+ }
+ if txType != "Contract" {
+ return nil, errors.New("not a contract tx")
+ }
+ // ctrAddr, err := jsonparser.GetString([]byte(txDetailStr), "result", "ContractAddress")
+ // if err != nil {
+ // return nil, err
+ // }
+ // if ctrAddr != c.address {
+ // return nil, errors.New("the event is not belong to current contract")
+ // }
+ ContractLogs, err := jsonparser.GetString([]byte(txDetailStr), "result", "meta", "ContractLogs")
+ if err != nil {
+ return nil, err
+ }
+
+ return c.GetPastEventByCtrLog(ContractLogs)
+}
+
+// UnpackLog unpacks a retrieved log into the provided output structure.
+func (c *BoundContract) UnpackLog(out interface{}, event string, log data.Log) error {
+ if log.Topics[0] != c.abi.Events[event].ID {
+ return fmt.Errorf("event signature mismatch")
+ }
+ if len(log.Data) > 0 {
+ if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
+ return err
+ }
+ }
+ var indexed abi.Arguments
+ for _, arg := range c.abi.Events[event].Inputs {
+ if arg.Indexed {
+ indexed = append(indexed, arg)
+ }
+ }
+ return abi.ParseTopics(out, indexed, log.Topics[1:])
+}
+
+// // UnpackLogIntoMap unpacks a retrieved log into the provided map.
+// func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
+// if log.Topics[0] != c.abi.Events[event].ID {
+// return fmt.Errorf("event signature mismatch")
+// }
+// if len(log.Data) > 0 {
+// if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
+// return err
+// }
+// }
+// var indexed abi.Arguments
+// for _, arg := range c.abi.Events[event].Inputs {
+// if arg.Indexed {
+// indexed = append(indexed, arg)
+// }
+// }
+// return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
+// }
+
+// // ensureContext is a helper method to ensure a context is not nil, even if the
+// // user specified it as such.
+// func ensureContext(ctx context.Context) context.Context {
+// if ctx == nil {
+// return context.Background()
+// }
+// return ctx
+// }
diff --git a/core/ripple.go b/core/ripple.go
index f34e1ae..c053571 100644
--- a/core/ripple.go
+++ b/core/ripple.go
@@ -2,7 +2,11 @@ package core
import (
"fmt"
+ "strconv"
+ "github.com/buger/jsonparser"
+
+ . "github.com/ChainSQL/go-chainsql-api/data"
"github.com/ChainSQL/go-chainsql-api/net"
)
@@ -10,6 +14,27 @@ import (
type Base struct {
}
+//OpInfo is the opearting details
+// type TransactionRequest struct {
+// TransactionType string
+// Amount Amount
+// Destination string
+// Query []interface{}
+// }
+
+//TxInfo is the opearting details
+type TxInfo struct {
+ Raw string
+ TxType TransactionType
+ Query []interface{}
+}
+
+// type Amount struct {
+// Value string `json:"value"`
+// Currency string `json:"currency"`
+// Account string `json:"account"`
+// }
+
func (b *Base) Say() {
fmt.Println("base")
}
@@ -22,6 +47,7 @@ func (b *Base) Say2() {
type Ripple struct {
*Base
client *net.Client
+ op *TxInfo
SubmitBase
}
@@ -29,17 +55,116 @@ func (r *Ripple) Say() {
fmt.Println("Ripple")
}
-func NewRipple() *Ripple {
+func NewRipple(client *net.Client) *Ripple {
ripple := &Ripple{
Base: &Base{},
- client: net.NewClient(),
+ client: client,
+ op: &TxInfo{
+ Query: make([]interface{}, 0),
+ },
}
ripple.SubmitBase.client = ripple.client
ripple.SubmitBase.IPrepare = ripple
return ripple
}
-func (r *Ripple) Pay(accountId string, value string) *Ripple {
+func (r *Ripple) Pay(accountId string, value int64) *Ripple {
+ r.op.TxType = PAYMENT
+ r.op.Raw = "{\"AccountId\": \"" + accountId + "\", \"Value\": \"" + strconv.FormatInt(value, 10) + "\"}"
return r
}
+
+func (r *Ripple) pay(raw string) (Signer, error) {
+ accountId, _ := jsonparser.GetString([]byte(raw), "AccountId")
+ strValue, _ := jsonparser.GetString([]byte(raw), "Value")
+ value, _ := strconv.ParseInt(strValue, 10, 64)
+
+ valueTemp, _ := NewNativeValue(value)
+ currency_zxc, _ := NewCurrency("ZXC")
+ amount := Amount{
+ Value: valueTemp,
+ Currency: currency_zxc,
+ }
+ return r.PayToNode(accountId, amount)
+}
+
+func (r *Ripple) PayToNode(accountId string, amount Amount) (Signer, error) {
+
+ // if !amount.Currency.IsNative() {
+ // accountData, err := r.client.GetAccountInfo(string(amount.Issuer))
+ // if err != nil {
+ // log.Println("get issuer %s", err)
+ // }
+
+ // if accountData != "" {
+ // //var feeMin, feeMax = "", ""
+ // //var lFeeRate = Value(0)
+ // var mapObj map[string]interface{}
+ // va := amount.Value
+ // json.Unmarshal([]byte(accountData), &mapObj)
+ // feeMin := mapObj["TransferFeeMin"].(Value)
+ // feeMax := mapObj["TransferFeeMax"].(Value)
+ // lFeeRate := mapObj["TransferRate"].(Value)
+ // fee := Value()
+ // if feeMin.IsZero() || feeMax.IsZero() || lFeeRate.IsZero() {
+ // if feeMin == feeMax {
+ // fee = feeMin.Float()
+ // } else if !lFeeRate.IsZero() {
+ // // fee = FloatOperation.accMul(parseFloat(value), data.rate - 1);
+ // fee = va.Multiply(lFeeRate)
+ // if !feeMin.IsZero() {
+ // if
+ // fee =
+ // }
+ // if feeMax.IsZero() {
+ // fee = Math.min(fee, parseFloat(data.max))
+ // }
+ // //
+ // value = value.add(fee)
+ // }
+ // }
+ // }
+ // }
+ destination, err := NewAccountFromAddress(accountId)
+ if err != nil {
+ return nil, err
+ }
+ payment := &Payment{
+ //SendMax: nil,
+ Destination: *destination,
+ Amount: amount,
+ }
+ payment.TransactionType = PAYMENT
+ account, err := NewAccountFromAddress(r.client.Auth.Address)
+ if err != nil {
+ return nil, err
+ }
+ payment.Account = *account
+ seq, err := net.PrepareRipple(r.client)
+ if err != nil {
+ return nil, err
+ }
+ payment.Sequence = seq
+ valueTemp, _ := NewNativeValue(10)
+ payment.Fee = *valueTemp
+ var sign Signer = payment
+ return sign, nil
+}
+
+//PrepareTx prepare tx json for submit
+func (r *Ripple) PrepareTx() (Signer, error) {
+ var tx Signer
+ var err error
+ switch r.op.TxType {
+ case PAYMENT:
+ tx, err = r.pay(r.op.Raw)
+ break
+ default:
+ }
+ if err != nil {
+ return nil, err
+ }
+ return tx, nil
+
+}
diff --git a/core/submit.go b/core/submit.go
index 4fad1ba..811f2fd 100644
--- a/core/submit.go
+++ b/core/submit.go
@@ -1,12 +1,12 @@
package core
import (
- "encoding/json"
"fmt"
"log"
"strings"
"sync"
+ . "github.com/ChainSQL/go-chainsql-api/common"
"github.com/ChainSQL/go-chainsql-api/crypto"
. "github.com/ChainSQL/go-chainsql-api/data"
"github.com/ChainSQL/go-chainsql-api/export"
@@ -25,14 +25,6 @@ type TxSigned struct {
hash string
}
-// TxResult is tx submit response
-type TxResult struct {
- Status string `json:"status"`
- TxHash string `json:"hash"`
- ErrorCode string `json:"error,omitempty"`
- ErrorMessage string `json:"errorMessage,omitempty"`
-}
-
// IPrepare is an interface that a struct call submit() method must implment
type IPrepare interface {
PrepareTx() (Signer, error)
@@ -46,12 +38,21 @@ type SubmitBase struct {
IPrepare
}
+// // Submit submit a tx with a cocurrent expect
+// func (s *SubmitBase) Submit(cond string) string {
+// s.expect = cond
+// ret := s.doSubmit()
+// jsonRet, _ := json.Marshal(ret)
+// return string(jsonRet)
+// }
// Submit submit a tx with a cocurrent expect
-func (s *SubmitBase) Submit(cond string) string {
- s.expect = cond
- ret := s.doSubmit()
- jsonRet, _ := json.Marshal(ret)
- return string(jsonRet)
+func (s *SubmitBase) Submit(cond string) (txRet *TxResult) {
+ if cond == "" {
+ s.expect = util.SendSuccess
+ } else {
+ s.expect = cond
+ }
+ return s.doSubmit()
}
//SubmitAsync submit a transaction and got response asynchronously
@@ -83,8 +84,15 @@ func (s *SubmitBase) doSubmit() *TxResult {
ErrorMessage: err.Error(),
}
}
+ /*var hasher hash.Hash
+ if key.Type() == common.SoftGMAlg {
+ hasher = sm3.New()
+ }else {
+ hasher = sha512.New()
+ }
+ */
sequenceZero := uint32(0)
- err = Sign(tx, key, &sequenceZero)
+ err = Sign(tx, key, &sequenceZero, key.Type())
if err != nil {
log.Printf("doSubmit error:%s\n", err)
return &TxResult{
@@ -93,16 +101,8 @@ func (s *SubmitBase) doSubmit() *TxResult {
}
}
- _, blob, err := Raw(tx)
- if err != nil {
- log.Printf("doSubmit error:%s\n", err)
- return &TxResult{
- ErrorCode: "errSerialize",
- ErrorMessage: err.Error(),
- }
- }
txSigned := &TxSigned{
- blob: fmt.Sprintf("%X", blob),
+ blob: fmt.Sprintf("%X", *tx.GetBlob()),
hash: string(crypto.B2H32(*tx.GetHash())),
}
diff --git a/core/table.go b/core/table.go
index 41c9bc9..8e4399e 100644
--- a/core/table.go
+++ b/core/table.go
@@ -142,7 +142,7 @@ func (t *Table) Request() (string, error) {
if t.client.ServerInfo.Updated {
data.LedgerIndex = t.client.ServerInfo.LedgerIndex
} else {
- ledgerIndex, err := t.client.GetLedgerVersion()
+ ledgerIndex, err := t.client.GetLedgerCurrent()
if err != nil {
return "", err
}
@@ -181,22 +181,11 @@ func (t *Table) PrepareTx() (Signer, error) {
tx.Account = *account
tx.Owner = *owner
tx.Sequence = seq
- var fee int64 = 10
- if t.client.ServerInfo.Updated {
- last := uint32(t.client.ServerInfo.LedgerIndex + 20)
- tx.LastLedgerSequence = &last
- fee = int64(t.client.ServerInfo.ComputeFee())
- } else {
- ledgerIndex, err := t.client.GetLedgerVersion()
- if err != nil {
- return nil, err
- }
- last := uint32(ledgerIndex + 20)
- tx.LastLedgerSequence = &last
-
- fee = 50
+ fee, lastLedgerSeq, err := net.PrepareLastLedgerSeqAndFee(t.client)
+ if err != nil {
+ return nil, err
}
-
+ tx.LastLedgerSequence = &lastLedgerSeq
fee += util.GetExtraFee(t.op.Raw, t.client.ServerInfo.DropsPerByte)
finalFee, err := NewNativeValue(fee)
if err != nil {
diff --git a/crypto/account.go b/crypto/account.go
index e7e5a9a..2367e17 100644
--- a/crypto/account.go
+++ b/crypto/account.go
@@ -2,19 +2,31 @@ package crypto
import (
"crypto/rand"
+ "encoding/binary"
"encoding/json"
"fmt"
"log"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
)
//Account define the account format
type Account struct {
- Address string `json:"address"`
- PublicKey string `json:"publicKey"`
- PublicKeyHex string `json:"publicKeyHex"`
- PrivateKey string `json:"privateKey"`
+ Address string `json:"address"`
+ PublicKeyBase58 string `json:"publicKeyBase58"`
+ PublicKeyHex string `json:"publicKeyHex"`
+ PrivateSeed string `json:"privateSeed"`
+ PrivateKey interface{} `json:"privateKey"`
+ PublicKey interface{} `json:"publicKey"`
+}
+
+type SeedKey struct {
+ Seed string `json:"seed"`
+ PublicKey string `json:"publicKey"`
}
+//生成特殊地址仍然使用此方法
func GenerateAccount(args ...string) (string, error) {
var seed Hash
var err error
@@ -43,11 +55,22 @@ func GenerateAccount(args ...string) (string, error) {
sequenceZero := uint32(0)
account, _ := AccountId(key, &sequenceZero)
publicKey, _ := AccountPublicKey(key, &sequenceZero)
+ pk, err := key.PK(&sequenceZero)
+ if err != nil {
+ return "", err
+ }
+
+ pub, err := key.PUB(&sequenceZero)
+ if err != nil {
+ return "", err
+ }
generated := Account{
- Address: account.String(),
- PublicKey: publicKey.String(),
- PublicKeyHex: fmt.Sprintf("%X", key.Public(&sequenceZero)),
- PrivateKey: seed.String(),
+ Address: account.String(),
+ PublicKeyBase58: publicKey.String(),
+ PublicKeyHex: fmt.Sprintf("%X", key.Public(&sequenceZero)),
+ PrivateSeed: seed.String(),
+ PrivateKey: pk,
+ PublicKey: pub,
}
jsonStr, err := json.Marshal(generated)
if err != nil {
@@ -56,8 +79,72 @@ func GenerateAccount(args ...string) (string, error) {
return string(jsonStr), nil
}
-func ValidationCreate() (string, error) {
- generated := Account{}
+func GenerateAddress(options string) (string, error) {
+ var seed *Seed
+ var err error
+ var key Key
+ if strings.Contains(options, "secret") && !strings.Contains(options, "algorithm") {
+ return "", fmt.Errorf("Invalid parameter")
+ }
+ seed, err = GenerateSeed(options)
+ if err != nil {
+ return "", err
+ }
+ sVersion := seed.version
+ switch sVersion {
+ case common.Ed25519:
+ key, err = NewEd25519Key(seed.SeedHash.Payload())
+ break
+ case common.SoftGMAlg:
+ // key, err = GenerateKeyPair(seed)
+ break
+ case common.ECDSA:
+ key, err = NewECDSAKey(seed.SeedHash.Payload())
+ break
+ default:
+ key, err = NewECDSAKey(seed.SeedHash.Payload())
+ }
+ if err != nil {
+ return "", err
+ }
+
+ sequenceZero := uint32(0)
+ account, err := AccountId(key, &sequenceZero)
+ if err != nil {
+ return "", err
+ }
+ publicKey, err := AccountPublicKey(key, &sequenceZero)
+ if err != nil {
+ return "", err
+ }
+ var privSeed Hash
+ if sVersion == common.SoftGMAlg {
+ privSeed, err = AccountPrivateKey(key, &sequenceZero)
+ if err != nil {
+ return "", err
+ }
+ } else {
+ privSeed = seed.SeedHash
+ }
+ pk, err := key.PK(&sequenceZero)
+ if err != nil {
+ return "", err
+ }
+
+ pub, err := key.PUB(&sequenceZero)
+ if err != nil {
+ return "", err
+ }
+
+ generated := Account{
+ Address: account.String(),
+ PublicKeyBase58: publicKey.String(),
+ PublicKeyHex: fmt.Sprintf("%X", key.Public(&sequenceZero)),
+ PrivateSeed: privSeed.String(),
+ PrivateKey: pk,
+ PublicKey: pub,
+ }
+
jsonStr, err := json.Marshal(generated)
if err != nil {
return "", err
@@ -65,8 +152,115 @@ func ValidationCreate() (string, error) {
return string(jsonStr), nil
}
-func GetAccountInfo(address string) (string, error) {
- generated := Account{}
+func GenerateAddressObj(options string) (*Account, error) {
+ var seed *Seed
+ var err error
+ var key Key
+ if strings.Contains(options, "secret") && !strings.Contains(options, "algorithm") {
+ return nil, fmt.Errorf("Invalid parameter")
+ }
+ seed, err = GenerateSeed(options)
+ if err != nil {
+ return nil, err
+ }
+ sVersion := seed.version
+ sequenceZero := uint32(0)
+ switch sVersion {
+ case common.Ed25519:
+ key, err = NewEd25519Key(seed.SeedHash.Payload())
+ break
+ case common.SoftGMAlg:
+ // key, err = GenerateKeyPair(seed)
+ break
+ case common.ECDSA:
+ key1 := &ecdsaKey{}
+ key1, err = NewECDSAKey(seed.SeedHash.Payload())
+ key = key1.GenerateEcdsaKey(sequenceZero)
+ break
+ default:
+ key, err = NewECDSAKey(seed.SeedHash.Payload())
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ account, err := AccountId(key, nil)
+ if err != nil {
+ return nil, err
+ }
+ publicKey, err := AccountPublicKey(key, nil)
+ if err != nil {
+ return nil, err
+ }
+ var privSeed Hash
+ if sVersion == common.SoftGMAlg {
+ privSeed, err = AccountPrivateKey(key, nil)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ privSeed = seed.SeedHash
+ }
+ pk, err := key.PK(nil)
+ if err != nil {
+ return nil, err
+ }
+
+ pub, err := key.PUB(nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Account{
+ Address: account.String(),
+ PublicKeyBase58: publicKey.String(),
+ PublicKeyHex: fmt.Sprintf("%X", key.Public(nil)),
+ PrivateSeed: privSeed.String(),
+ PrivateKey: pk,
+ PublicKey: pub,
+ }, nil
+}
+
+func CreateContractAddr(ctrOwnerAddr string, sequence uint32) (string, error) {
+ deBase58Addr, error := newHashFromString(ctrOwnerAddr)
+ if error != nil {
+ return "", error
+ }
+
+ seqByte := make([]byte, 4)
+ binary.BigEndian.PutUint32(seqByte, sequence)
+ finalByte := append(deBase58Addr.Payload(), seqByte...)
+
+ contractAddr, error := NewAccountId(Sha256RipeMD160(finalByte))
+ if error != nil {
+ return "", error
+ }
+ return contractAddr.String(), nil
+}
+
+func ValidationCreate() (string, error) {
+ var seed Hash
+ var err error
+ var key *ecdsaKey
+ rndBytes := make([]byte, 16)
+ if _, err := rand.Read(rndBytes); err != nil {
+ return "", err
+ }
+ seed, err = GenerateFamilySeed(string(rndBytes))
+ if err != nil {
+ return "", err
+ }
+ key, err = NewECDSAKey(seed.Payload())
+ if err != nil {
+ log.Println(err)
+ return "", err
+ }
+ publicKey, _ := NodePublicKey(key)
+ generated := SeedKey{
+ Seed: seed.String(),
+ PublicKey: publicKey.String(),
+ }
jsonStr, err := json.Marshal(generated)
if err != nil {
return "", err
diff --git a/crypto/base58.go b/crypto/base58.go
index 4fbe1da..c96999c 100644
--- a/crypto/base58.go
+++ b/crypto/base58.go
@@ -1,86 +1,86 @@
-package crypto
-
-import (
- "bytes"
- "fmt"
- "math/big"
- "strings"
-)
-
-// Purloined from https://github.com/conformal/btcutil/
-
-var bigRadix = big.NewInt(58)
-var bigZero = big.NewInt(0)
-
-// Base58Decode decodes a modified base58 string to a byte slice and checks checksum.
-func Base58Decode(b, alphabet string) ([]byte, error) {
- if len(b) < 5 {
- return nil, fmt.Errorf("Base58 string too short: %s", b)
- }
- answer := big.NewInt(0)
- j := big.NewInt(1)
-
- for i := len(b) - 1; i >= 0; i-- {
- tmp := strings.IndexAny(alphabet, string(b[i]))
- if tmp == -1 {
- return nil, fmt.Errorf("Bad Base58 string: %s", b)
- }
- idx := big.NewInt(int64(tmp))
- tmp1 := big.NewInt(0)
- tmp1.Mul(j, idx)
-
- answer.Add(answer, tmp1)
- j.Mul(j, bigRadix)
- }
-
- tmpval := answer.Bytes()
-
- var numZeros int
- for numZeros = 0; numZeros < len(b); numZeros++ {
- if b[numZeros] != alphabet[0] {
- break
- }
- }
- flen := numZeros + len(tmpval)
- val := make([]byte, flen, flen)
- copy(val[numZeros:], tmpval)
-
- // Check checksum
- checksum := DoubleSha256(val[0 : len(val)-4])
- expected := val[len(val)-4:]
- if !bytes.Equal(checksum[0:4], expected) {
- return nil, fmt.Errorf("Bad Base58 checksum: %v expected %v", checksum, expected)
- }
- return val, nil
-}
-
-// Base58Encode encodes a byte slice to a modified base58 string.
-func Base58Encode(b []byte, alphabet string) string {
- checksum := DoubleSha256(b)
- b = append(b, checksum[0:4]...)
- x := new(big.Int)
- x.SetBytes(b)
-
- answer := make([]byte, 0)
- for x.Cmp(bigZero) > 0 {
- mod := new(big.Int)
- x.DivMod(x, bigRadix, mod)
- answer = append(answer, alphabet[mod.Int64()])
- }
-
- // leading zero bytes
- for _, i := range b {
- if i != 0 {
- break
- }
- answer = append(answer, alphabet[0])
- }
-
- // reverse
- alen := len(answer)
- for i := 0; i < alen/2; i++ {
- answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
- }
-
- return string(answer)
-}
+package crypto
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+ "strings"
+)
+
+// Purloined from https://github.com/conformal/btcutil/
+
+var bigRadix = big.NewInt(58)
+var bigZero = big.NewInt(0)
+
+// Base58Decode decodes a modified base58 string to a byte slice and checks checksum.
+func Base58Decode(b, alphabet string) ([]byte, error) {
+ if len(b) < 5 {
+ return nil, fmt.Errorf("Base58 string too short: %s", b)
+ }
+ answer := big.NewInt(0)
+ j := big.NewInt(1)
+
+ for i := len(b) - 1; i >= 0; i-- {
+ tmp := strings.IndexAny(alphabet, string(b[i]))
+ if tmp == -1 {
+ return nil, fmt.Errorf("Bad Base58 string: %s", b)
+ }
+ idx := big.NewInt(int64(tmp))
+ tmp1 := big.NewInt(0)
+ tmp1.Mul(j, idx)
+
+ answer.Add(answer, tmp1)
+ j.Mul(j, bigRadix)
+ }
+
+ tmpval := answer.Bytes()
+
+ var numZeros int
+ for numZeros = 0; numZeros < len(b); numZeros++ {
+ if b[numZeros] != alphabet[0] {
+ break
+ }
+ }
+ flen := numZeros + len(tmpval)
+ val := make([]byte, flen, flen)
+ copy(val[numZeros:], tmpval)
+
+ // Check checksum
+ checksum := DoubleSha256(val[0 : len(val)-4])
+ expected := val[len(val)-4:]
+ if !bytes.Equal(checksum[0:4], expected) {
+ return nil, fmt.Errorf("Bad Base58 checksum: %v expected %v", checksum, expected)
+ }
+ return val, nil
+}
+
+// Base58Encode encodes a byte slice to a modified base58 string.
+func Base58Encode(b []byte, alphabet string) string {
+ checksum := DoubleSha256(b)
+ b = append(b, checksum[0:4]...)
+ x := new(big.Int)
+ x.SetBytes(b)
+
+ answer := make([]byte, 0)
+ for x.Cmp(bigZero) > 0 {
+ mod := new(big.Int)
+ x.DivMod(x, bigRadix, mod)
+ answer = append(answer, alphabet[mod.Int64()])
+ }
+
+ // leading zero bytes
+ for _, i := range b {
+ if i != 0 {
+ break
+ }
+ answer = append(answer, alphabet[0])
+ }
+
+ // reverse
+ alen := len(answer)
+ for i := 0; i < alen/2; i++ {
+ answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
+ }
+
+ return string(answer)
+}
diff --git a/crypto/const.go b/crypto/const.go
index 12ebcb1..ad5db1d 100644
--- a/crypto/const.go
+++ b/crypto/const.go
@@ -21,6 +21,13 @@ const (
RIPPLE_ACCOUNT_PUBLIC HashVersion = 35
)
+// const (
+// ED25519 = 0
+// K256 = 1
+// SM = 3
+// SOFT_SM = 4
+// )
+
var hashTypes = [...]struct {
Description string
Prefix byte
@@ -31,6 +38,6 @@ var hashTypes = [...]struct {
RIPPLE_NODE_PUBLIC: {"Validation public key for node.", 'n', 33, 53},
RIPPLE_NODE_PRIVATE: {"Validation private key for node.", 'p', 32, 52},
RIPPLE_FAMILY_SEED: {"Family seed.", 's', 16, 29},
- RIPPLE_ACCOUNT_PRIVATE: {"Account private key.", 'p', 32, 52},
- RIPPLE_ACCOUNT_PUBLIC: {"Account public key.", 'a', 33, 53},
+ RIPPLE_ACCOUNT_PRIVATE: {"Account private key.", 'p', 65, 52},
+ RIPPLE_ACCOUNT_PUBLIC: {"Account public key.", 'a', 65, 53},
}
diff --git a/crypto/ecdsa.go b/crypto/ecdsa.go
index 0ccb0b4..eecf2ce 100644
--- a/crypto/ecdsa.go
+++ b/crypto/ecdsa.go
@@ -5,6 +5,8 @@ import (
"encoding/binary"
"math/big"
+ "github.com/ChainSQL/go-chainsql-api/common"
+
"github.com/btcsuite/btcd/btcec"
)
@@ -21,9 +23,21 @@ type ecdsaKey struct {
func newKey(seed []byte) *btcec.PrivateKey {
inc := big.NewInt(0).SetBytes(seed)
inc.Lsh(inc, 32)
+ accLen := len(seed) + 4
for key := big.NewInt(0); ; inc.Add(inc, one) {
- key.SetBytes(Sha512Half(inc.Bytes()))
+ incBytes := inc.Bytes()
+
+ paddLen := accLen - len(incBytes)
+ finalBytes := make([]byte, accLen)
+ if paddLen != 0 {
+ tempBytes := make([]byte, paddLen)
+ finalBytes = append(tempBytes[:], incBytes[:]...)
+ } else {
+ finalBytes = incBytes
+ }
+ key.SetBytes(Sha512Half(finalBytes))
if key.Cmp(zero) > 0 && key.Cmp(order) < 0 {
+ // privKey, _ := btcec.PrivKeyFromBytes(elliptic.S256(), key.Bytes())
privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), key.Bytes())
return privKey
}
@@ -41,7 +55,7 @@ func NewECDSAKey(seed []byte) (*ecdsaKey, error) {
return &ecdsaKey{newKey(seed)}, nil
}
-func (k *ecdsaKey) generateKey(sequence uint32) *btcec.PrivateKey {
+func (k *ecdsaKey) GenerateKey(sequence uint32) *btcec.PrivateKey {
seed := make([]byte, btcec.PubKeyBytesLenCompressed+4)
copy(seed, k.PubKey().SerializeCompressed())
binary.BigEndian.PutUint32(seed[btcec.PubKeyBytesLenCompressed:], sequence)
@@ -51,6 +65,16 @@ func (k *ecdsaKey) generateKey(sequence uint32) *btcec.PrivateKey {
return key
}
+func (k *ecdsaKey) GenerateEcdsaKey(sequence uint32) *ecdsaKey {
+ seed := make([]byte, btcec.PubKeyBytesLenCompressed+4)
+ copy(seed, k.PubKey().SerializeCompressed())
+ binary.BigEndian.PutUint32(seed[btcec.PubKeyBytesLenCompressed:], sequence)
+ key := newKey(seed)
+ key.D.Add(key.D, k.D).Mod(key.D, order)
+ key.X, key.Y = key.ScalarBaseMult(key.D.Bytes())
+ return &ecdsaKey{key}
+}
+
func (k *ecdsaKey) Id(sequence *uint32) []byte {
if sequence == nil {
return Sha256RipeMD160(k.PubKey().SerializeCompressed())
@@ -62,14 +86,33 @@ func (k *ecdsaKey) Private(sequence *uint32) []byte {
if sequence == nil {
return k.D.Bytes()
}
- return k.generateKey(*sequence).D.Bytes()
+ return k.GenerateKey(*sequence).D.Bytes()
+}
+
+func (k *ecdsaKey) PK(sequence *uint32) (interface{}, error) {
+ //if sequence == nil {
+ // return k.PrivateKey, nil
+ //}
+ //return k.GenerateKey(*sequence), nil
+ return k.PrivateKey.ToECDSA(), nil
}
func (k *ecdsaKey) Public(sequence *uint32) []byte {
if sequence == nil {
return k.PubKey().SerializeCompressed()
}
- return k.generateKey(*sequence).PubKey().SerializeCompressed()
+
+ return k.GenerateKey(*sequence).PubKey().SerializeCompressed()
}
+func (k *ecdsaKey) PUB(sequence *uint32) (interface{}, error) {
+ //if sequence == nil {
+ // return k.PublicKey, nil
+ //}
+ //return k.GenerateKey(*sequence).PublicKey, nil
+ return k.PublicKey, nil
+}
+func (k *ecdsaKey) Type() common.KeyType {
+ return common.ECDSA
+}
diff --git a/crypto/ed25519.go b/crypto/ed25519.go
index 7e31549..bc0aada 100644
--- a/crypto/ed25519.go
+++ b/crypto/ed25519.go
@@ -4,15 +4,18 @@ import (
"bytes"
"crypto/ed25519"
"crypto/rand"
+ "fmt"
+ "github.com/ChainSQL/go-chainsql-api/common"
)
type ed25519key struct {
priv ed25519.PrivateKey
+ pub ed25519.PublicKey
}
func checkSequenceIsNil(seq *uint32) {
if seq != nil {
- panic("Ed25519 keys do not support account families")
+ fmt.Errorf("Ed25519 keys do not support account families")
}
}
@@ -26,19 +29,36 @@ func (e *ed25519key) Public(seq *uint32) []byte {
return append([]byte{0xED}, e.priv[32:]...)
}
+func (e *ed25519key) PUB(seq *uint32) (interface{}, error) {
+ //checkSequenceIsNil(seq)
+ //return append([]byte{0xED}, e.priv[32:]...), nil
+ return e.pub, nil
+}
+
func (e *ed25519key) Private(seq *uint32) []byte {
checkSequenceIsNil(seq)
return e.priv[:]
}
+func (e *ed25519key) PK(seq *uint32) (interface{}, error) {
+ //checkSequenceIsNil(seq)
+ //return e.priv[:], nil
+ return e.priv, nil
+}
+
+func (k *ed25519key) Type() common.KeyType {
+ return common.Ed25519
+}
+
func NewEd25519Key(seed []byte) (*ed25519key, error) {
r := rand.Reader
if seed != nil {
r = bytes.NewReader(Sha512Half(seed))
}
- _, priv, err := ed25519.GenerateKey(r)
+ pub, priv, err := ed25519.GenerateKey(r)
if err != nil {
return nil, err
}
- return &ed25519key{priv: priv}, nil
+ key := &ed25519key{priv: priv, pub: pub}
+ return key, nil
}
diff --git a/crypto/hash.go b/crypto/hash.go
index 392e317..b049d09 100644
--- a/crypto/hash.go
+++ b/crypto/hash.go
@@ -8,7 +8,7 @@ import (
// First byte is the network
// Second byte is the version
// Remaining bytes are the payload
-type hash []byte
+type hashType []byte
func NewRippleHash(s string) (Hash, error) {
// Special case which will deal short addresses
@@ -36,6 +36,17 @@ func NewRippleHashCheck(s string, version HashVersion) (Hash, error) {
return hash, nil
}
+// Checks hash matches expected version
+func NewRippleSeed(s string, version HashVersion) (*Seed, error) {
+ keySeed := &Seed{}
+ hash, err := NewRippleHashCheck(s, version)
+ if err != nil {
+ return nil, err
+ }
+ keySeed.SeedHash = hash
+ return keySeed, nil
+}
+
func NewAccountId(b []byte) (Hash, error) {
return newHash(b, RIPPLE_ACCOUNT_ID)
}
@@ -89,7 +100,7 @@ func newHash(b []byte, version HashVersion) (Hash, error) {
if len(b) > n {
return nil, fmt.Errorf("Hash is wrong size, expected: %d got: %d", n, len(b))
}
- return append(hash{byte(version)}, b...), nil
+ return append(hashType{byte(version)}, b...), nil
}
func newHashFromString(s string) (Hash, error) {
@@ -97,24 +108,24 @@ func newHashFromString(s string) (Hash, error) {
if err != nil {
return nil, err
}
- return hash(decoded[:len(decoded)-4]), nil
+ return hashType(decoded[:len(decoded)-4]), nil
}
-func (h hash) String() string {
- b := append(hash{byte(h.Version())}, h.Payload()...)
+func (h hashType) String() string {
+ b := append(hashType{byte(h.Version())}, h.Payload()...)
return Base58Encode(b, ALPHABET)
}
-func (h hash) Version() HashVersion {
+func (h hashType) Version() HashVersion {
return HashVersion(h[0])
}
-func (h hash) Payload() []byte {
+func (h hashType) Payload() []byte {
return h[1:]
}
// Return a slice of the payload with leading zeroes omitted
-func (h hash) PayloadTrimmed() []byte {
+func (h hashType) PayloadTrimmed() []byte {
payload := h.Payload()
for i := range payload {
if payload[i] != 0 {
@@ -124,16 +135,16 @@ func (h hash) PayloadTrimmed() []byte {
return payload[len(payload)-1:]
}
-func (h hash) Value() *big.Int {
+func (h hashType) Value() *big.Int {
return big.NewInt(0).SetBytes(h.Payload())
}
-func (h hash) MarshalText() ([]byte, error) {
+func (h hashType) MarshalText() ([]byte, error) {
return []byte(h.String()), nil
}
-func (h hash) Clone() Hash {
- c := make(hash, len(h))
+func (h hashType) Clone() Hash {
+ c := make(hashType, len(h))
copy(c, h)
return c
}
diff --git a/crypto/interface.go b/crypto/interface.go
index b446935..396d49e 100644
--- a/crypto/interface.go
+++ b/crypto/interface.go
@@ -1,11 +1,18 @@
package crypto
-import "math/big"
+import (
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "math/big"
+)
type Key interface {
Private(*uint32) []byte
Id(*uint32) []byte
Public(*uint32) []byte
+ Type() common.KeyType
+ PK(*uint32) (interface{}, error)
+ PUB(*uint32) (interface{}, error)
+ //Hasher() Hash
}
type Hash interface {
diff --git a/crypto/key_test.go b/crypto/key_test.go
index 84acafa..0f847c0 100644
--- a/crypto/key_test.go
+++ b/crypto/key_test.go
@@ -18,8 +18,9 @@ func checkHash(h Hash, err error) string {
return h.String()
}
-func checkSignature(c *C, privateKey, publicKey, hash, msg []byte) bool {
- sig, err := Sign(privateKey, hash, msg)
+func checkSignature(c *C, key Key, publicKey, hash, msg []byte) bool {
+ sequenceZero := uint32(0)
+ sig, err := Sign(key, hash, &sequenceZero, msg)
c.Assert(err, IsNil)
ok, err := Verify(publicKey, hash, msg, sig)
c.Assert(err, IsNil)
@@ -76,11 +77,11 @@ func (s *KeySuite) TestRippledVectors(c *C) {
msg := []byte("Hello, nurse!")
hash := Sha512Half(msg)
- c.Check(checkSignature(c, key.Private(nil), key.Public(nil), hash, msg), Equals, true)
- c.Check(checkSignature(c, key.Private(&sequenceZero), key.Public(&sequenceZero), hash, msg), Equals, true)
- c.Check(checkSignature(c, key.Private(&sequenceOne), key.Public(&sequenceOne), hash, msg), Equals, true)
- c.Check(checkSignature(c, key.Private(&sequenceOne), key.Public(&sequenceZero), hash, msg), Equals, false)
- c.Check(checkSignature(c, key.Private(&sequenceZero), key.Public(&sequenceOne), hash, msg), Equals, false)
+ c.Check(checkSignature(c, key, key.Public(nil), hash, msg), Equals, true)
+ c.Check(checkSignature(c, key, key.Public(&sequenceZero), hash, msg), Equals, true)
+ c.Check(checkSignature(c, key, key.Public(&sequenceOne), hash, msg), Equals, true)
+ c.Check(checkSignature(c, key, key.Public(&sequenceZero), hash, msg), Equals, false)
+ c.Check(checkSignature(c, key, key.Public(&sequenceOne), hash, msg), Equals, false)
}
@@ -102,8 +103,8 @@ func (s *KeySuite) TestEd25119(c *C) {
msg := []byte("Hello, nurse!")
hash := Sha512Half(msg)
- c.Check(checkSignature(c, key.Private(nil), key.Public(nil), hash, msg), Equals, true)
- c.Check(checkSignature(c, other.Private(nil), other.Public(nil), hash, msg), Equals, true)
- c.Check(checkSignature(c, key.Private(nil), other.Public(nil), hash, msg), Equals, false)
- c.Check(checkSignature(c, other.Private(nil), key.Public(nil), hash, msg), Equals, false)
+ c.Check(checkSignature(c, key, key.Public(nil), hash, msg), Equals, true)
+ c.Check(checkSignature(c, other, other.Public(nil), hash, msg), Equals, true)
+ c.Check(checkSignature(c, key, other.Public(nil), hash, msg), Equals, false)
+ c.Check(checkSignature(c, other, key.Public(nil), hash, msg), Equals, false)
}
diff --git a/crypto/seed.go b/crypto/seed.go
new file mode 100644
index 0000000..3cc14c5
--- /dev/null
+++ b/crypto/seed.go
@@ -0,0 +1,75 @@
+package crypto
+
+import (
+ "crypto/rand"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+
+ "github.com/buger/jsonparser"
+)
+
+type Seed struct {
+ SeedHash Hash
+ version common.KeyType
+}
+
+func GenerateSeed(options string) (*Seed, error) {
+ var err error
+ sVersion, _ := jsonparser.GetString([]byte(options), "algorithm")
+ var version common.KeyType
+ seed := &Seed{}
+ switch sVersion {
+ case "ed25519":
+ version = common.Ed25519
+ break
+ case "secp256k1":
+ version = common.ECDSA
+ break
+ case "softGMAlg":
+ version = common.SoftGMAlg
+ break
+ default:
+ version = common.ECDSA
+ }
+ seed.version = version
+ if version == common.SoftGMAlg {
+ if strings.Contains(options, "secret") {
+ secret, _ := (jsonparser.GetString([]byte(options), "secret"))
+ seed.SeedHash, err = newHashFromString(secret)
+ } else {
+ seed.SeedHash = nil
+ }
+ } else {
+ if strings.Contains(options, "secret") {
+ secret, _ := (jsonparser.GetString([]byte(options), "secret"))
+ seed.SeedHash, err = newHashFromString(secret)
+ } else {
+ rndBytes := make([]byte, 16)
+ if _, err := rand.Read(rndBytes); err != nil {
+ return nil, err
+ }
+ seed.SeedHash, err = GenerateFamilySeed(string(rndBytes))
+ }
+ }
+ return seed, err
+}
+
+func (s *Seed) GenerateKey(keyType common.KeyType) (Key, error) {
+ var (
+ key Key
+ err error
+ )
+ switch keyType {
+ case common.Ed25519:
+ key, err = NewEd25519Key(s.SeedHash.Payload())
+ case common.ECDSA:
+ key, err = NewECDSAKey(s.SeedHash.Payload())
+ case common.SoftGMAlg:
+ // key, err = GenerateKeyPairBySeed(s.SeedHash.Payload())
+ }
+ if err != nil {
+ return nil, err
+ }
+ return key, nil
+}
diff --git a/crypto/signature.go b/crypto/signature.go
index 61ff587..5ed2931 100644
--- a/crypto/signature.go
+++ b/crypto/signature.go
@@ -4,15 +4,18 @@ import (
"crypto/ed25519"
"fmt"
+ "github.com/ChainSQL/go-chainsql-api/common"
"github.com/btcsuite/btcd/btcec"
)
-func Sign(privateKey, hash, msg []byte) ([]byte, error) {
- switch len(privateKey) {
- case ed25519.PrivateKeySize:
- return signEd25519(privateKey, msg)
- case btcec.PrivKeyBytesLen:
- return signECDSA(privateKey, hash)
+func Sign(key Key, hash []byte, sequence *uint32, msg []byte) ([]byte, error) {
+ switch key.Type() {
+ case common.Ed25519:
+ return signEd25519(key.Private(sequence), msg)
+ case common.ECDSA:
+ return signECDSA(key.Private(sequence), hash)
+ // case common.SoftGMAlg:
+ // return signSoftSM(key.Private(sequence), msg)
default:
return nil, fmt.Errorf("Unknown private key format")
}
@@ -48,6 +51,7 @@ func verifyEd25519(pubKey, signature, msg []byte) (bool, error) {
// Returns DER encoded signature from input hash
func signECDSA(privateKey, hash []byte) ([]byte, error) {
+ // priv, _ := btcec.PrivKeyFromBytes(elliptic.S256(), privateKey)
priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), privateKey)
sig, err := priv.Sign(hash)
if err != nil {
@@ -68,3 +72,16 @@ func verifyECDSA(pubKey, signature, hash []byte) (bool, error) {
}
return sig.Verify(hash, pk), nil
}
+
+// func signSoftSM(privateKey, msg []byte) ([]byte, error) {
+// keyPair, err := PrivKeyFromBytes(privateKey)
+// if err != nil {
+// return nil, err
+// }
+// sign, err := SmSign(keyPair, msg)
+// if err != nil {
+// return nil, err
+// }
+// signByte, err := hex.DecodeString(sign) // 转码
+// return signByte, nil
+// }
diff --git a/crypto/util.go b/crypto/util.go
index 6914f46..65e8918 100644
--- a/crypto/util.go
+++ b/crypto/util.go
@@ -1,55 +1,90 @@
-package crypto
-
-import (
- "crypto/sha256"
- "crypto/sha512"
- "encoding/hex"
- "fmt"
-
- "golang.org/x/crypto/ripemd160"
-)
-
-// Write operations in a hash.Hash never return an error
-
-// Returns first 32 bytes of a SHA512 of the input bytes
-func Sha512Half(b []byte) []byte {
- hasher := sha512.New()
- hasher.Write(b)
- return hasher.Sum(nil)[:32]
-}
-
-// Returns first 16 bytes of a SHA512 of the input bytes
-func Sha512Quarter(b []byte) []byte {
- hasher := sha512.New()
- hasher.Write(b)
- return hasher.Sum(nil)[:16]
-}
-
-func DoubleSha256(b []byte) []byte {
- hasher := sha256.New()
- hasher.Write(b)
- sha := hasher.Sum(nil)
- hasher.Reset()
- hasher.Write(sha)
- return hasher.Sum(nil)
-}
-
-func Sha256RipeMD160(b []byte) []byte {
- ripe := ripemd160.New()
- sha := sha256.New()
- sha.Write(b)
- ripe.Write(sha.Sum(nil))
- return ripe.Sum(nil)
-}
-
-func H2B(s string) ([]byte, error) {
- return hex.DecodeString(s)
-}
-
-func B2H(b []byte) string {
- return fmt.Sprintf("%X", b)
-}
-
-func B2H32(b [32]byte) string {
- return fmt.Sprintf("%X", b)
-}
+package crypto
+
+import (
+ "crypto/sha256"
+ "crypto/sha512"
+ "encoding/hex"
+ "fmt"
+ "hash"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "golang.org/x/crypto/ripemd160"
+ "golang.org/x/crypto/sha3"
+)
+
+// Write operations in a hash.Hash never return an error
+
+// Returns first 32 bytes of a SHA512 of the input bytes
+func Sha512Half(b []byte) []byte {
+ hasher := sha512.New()
+ hasher.Write(b)
+ return hasher.Sum(nil)[:32]
+}
+
+// Returns first 16 bytes of a SHA512 of the input bytes
+func Sha512Quarter(b []byte) []byte {
+ hasher := sha512.New()
+ hasher.Write(b)
+ return hasher.Sum(nil)[:16]
+}
+
+func DoubleSha256(b []byte) []byte {
+ hasher := sha256.New()
+ hasher.Write(b)
+ sha := hasher.Sum(nil)
+ hasher.Reset()
+ hasher.Write(sha)
+ return hasher.Sum(nil)
+}
+
+func Sha256RipeMD160(b []byte) []byte {
+ ripe := ripemd160.New()
+ sha := sha256.New()
+ sha.Write(b)
+ ripe.Write(sha.Sum(nil))
+ return ripe.Sum(nil)
+}
+
+func H2B(s string) ([]byte, error) {
+ return hex.DecodeString(s)
+}
+
+func B2H(b []byte) string {
+ return fmt.Sprintf("%X", b)
+}
+
+func B2H32(b [32]byte) string {
+ return fmt.Sprintf("%X", b)
+}
+
+type KeccakState interface {
+ hash.Hash
+ Read([]byte) (int, error)
+}
+
+// NewKeccakState creates a new KeccakState
+func NewKeccakState() KeccakState {
+ return sha3.NewLegacyKeccak256().(KeccakState)
+}
+
+// Keccak256 calculates and returns the Keccak256 hash of the input data.
+func Keccak256(data ...[]byte) []byte {
+ b := make([]byte, 32)
+ d := NewKeccakState()
+ for _, b := range data {
+ d.Write(b)
+ }
+ d.Read(b)
+ return b
+}
+
+// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
+// converting it to an internal Hash data structure.
+func Keccak256Hash(data ...[]byte) (h common.Hash) {
+ d := NewKeccakState()
+ for _, b := range data {
+ d.Write(b)
+ }
+ d.Read(h[:])
+ return h
+}
diff --git a/data/amount.go b/data/amount.go
index cea36bd..df6279d 100644
--- a/data/amount.go
+++ b/data/amount.go
@@ -252,7 +252,7 @@ func NewExchangeRate(a, b *Amount) (ExchangeRate, error) {
return 0, nil
}
if rate.offset >= -100 || rate.offset <= 155 {
- panic("Impossible Rate")
+ return 0, fmt.Errorf("Impossible Rate")
}
return ExchangeRate(uint64(rate.offset+100)<<54 | uint64(rate.num)), nil
}
diff --git a/data/decoder.go b/data/decoder.go
index e31ae47..8a08d27 100644
--- a/data/decoder.go
+++ b/data/decoder.go
@@ -3,6 +3,7 @@ package data
import (
"errors"
"fmt"
+ "github.com/ChainSQL/go-chainsql-api/common"
"reflect"
)
@@ -81,7 +82,7 @@ func ReadTransaction(r Reader) (Transaction, error) {
// ReadTransactionAndMetadata combines the inputs from the two
// readers into a TransactionWithMetaData
-func ReadTransactionAndMetadata(tx, meta Reader, hash Hash256, ledger uint32) (*TransactionWithMetaData, error) {
+func ReadTransactionAndMetadata(tx, meta Reader, hash Hash256, ledger uint32, keyType common.KeyType) (*TransactionWithMetaData, error) {
t, err := ReadTransaction(tx)
if err != nil {
return nil, err
@@ -95,7 +96,7 @@ func ReadTransactionAndMetadata(tx, meta Reader, hash Hash256, ledger uint32) (*
return nil, err
}
*txm.GetHash() = hash
- if txm.Id, err = NodeId(txm); err != nil {
+ if txm.Id, err = NodeId(txm, keyType); err != nil {
return nil, err
}
return txm, nil
diff --git a/data/encoder.go b/data/encoder.go
index a020581..2b53ee1 100644
--- a/data/encoder.go
+++ b/data/encoder.go
@@ -4,47 +4,55 @@ import (
"bytes"
"crypto/sha512"
"fmt"
+ "hash"
"io"
"reflect"
"sort"
"strings"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
)
-func Raw(h Hashable) (Hash256, []byte, error) {
- return raw(h, h.Prefix(), false)
+func Raw(h Hashable, keyType common.KeyType) (Hash256, []byte, error) {
+ return raw(h, h.Prefix(), false, keyType)
}
-func NodeId(h Hashable) (Hash256, error) {
- nodeid, _, err := raw(h, h.Prefix(), false)
+func NodeId(h Hashable, keyType common.KeyType) (Hash256, error) {
+ nodeid, _, err := raw(h, h.Prefix(), false, keyType)
return nodeid, err
}
-func SigningHash(s Signer) (Hash256, []byte, error) {
- return raw(s, s.SigningPrefix(), true)
+func SigningHash(s Signer, keyType common.KeyType) (Hash256, []byte, error) {
+ return raw(s, s.SigningPrefix(), true, keyType)
}
-func Node(h Storer) (Hash256, []byte, error) {
+func Node(h Storer, keyType common.KeyType) (Hash256, []byte, error) {
var header bytes.Buffer
for _, v := range []interface{}{h.Ledger(), h.Ledger(), h.NodeType(), h.Prefix()} {
if err := write(&header, v); err != nil {
return zero256, nil, err
}
}
- key, value, err := raw(h, h.Prefix(), true)
+ key, value, err := raw(h, h.Prefix(), true, keyType)
if err != nil {
return zero256, nil, err
}
return key, append(header.Bytes(), value...), nil
}
-func raw(value interface{}, prefix HashPrefix, ignoreSigningFields bool) (Hash256, []byte, error) {
+func raw(value interface{}, prefix HashPrefix, ignoreSigningFields bool, keyType common.KeyType) (Hash256, []byte, error) {
buf := new(bytes.Buffer)
- hasher := sha512.New()
+ var hasher hash.Hash
+ if keyType == common.SoftGMAlg {
+ // hasher = sm3.New()
+ } else {
+ hasher = sha512.New()
+ }
multi := io.MultiWriter(buf, hasher)
if err := write(hasher, prefix); err != nil {
return zero256, nil, err
}
- if err := writeRaw(multi, value, ignoreSigningFields); err != nil {
+ if err := writeRaw(multi, value, ignoreSigningFields, keyType); err != nil {
return zero256, nil, err
}
var hash Hash256
@@ -53,7 +61,7 @@ func raw(value interface{}, prefix HashPrefix, ignoreSigningFields bool) (Hash25
}
// Disgusting node format and ordering handled here
-func writeRaw(w io.Writer, value interface{}, ignoreSigningFields bool) error {
+func writeRaw(w io.Writer, value interface{}, ignoreSigningFields bool, keyType common.KeyType) error {
switch v := value.(type) {
case *Ledger:
return write(w, v.LedgerHeader)
@@ -68,7 +76,7 @@ func writeRaw(w io.Writer, value interface{}, ignoreSigningFields bool) error {
return write(w, v)
}
case *TransactionWithMetaData:
- txid, tx, err := Raw(v.Transaction)
+ txid, tx, err := Raw(v.Transaction, keyType)
if err != nil {
return err
}
@@ -107,8 +115,12 @@ func encode(w io.Writer, value interface{}, ignoreSigningFields bool) error {
if ignoreSigningFields && e.SigningField() {
return nil
}
- if err := writeEncoding(w, e); err != nil {
- return err
+ // if err := writeEncoding(w, e); err != nil {
+ // return err
+ // }
+ err1 := writeEncoding(w, e)
+ if err1 != nil {
+ return err1
}
var err error
switch v2 := v.(type) {
diff --git a/data/factory.go b/data/factory.go
index cce0a10..a8b0ef8 100644
--- a/data/factory.go
+++ b/data/factory.go
@@ -43,6 +43,11 @@ const (
TRUST_SET TransactionType = 20
TABLE_LIST_SET TransactionType = 21
SQLSTATEMENT TransactionType = 22
+ SQLTRANSACTION TransactionType = 23
+ CONTRACT TransactionType = 24
+ SCHEMA_CREATE TransactionType = 25
+ SCHEMA_MODIFY TransactionType = 26
+ SCHEMA_DELETE TransactionType = 28
ACCOUNT_DELETE TransactionType = 51
AMENDMENT TransactionType = 100
SET_FEE TransactionType = 101
@@ -88,6 +93,9 @@ var TxFactory = [...]func() Transaction{
CHECK_CREATE: func() Transaction { return &CheckCreate{TxBase: TxBase{TransactionType: CHECK_CREATE}} },
CHECK_CASH: func() Transaction { return &CheckCash{TxBase: TxBase{TransactionType: CHECK_CASH}} },
CHECK_CANCEL: func() Transaction { return &CheckCancel{TxBase: TxBase{TransactionType: CHECK_CANCEL}} },
+ SCHEMA_CREATE: func() Transaction { return &SchemaCreate{TxBase: TxBase{TransactionType: SCHEMA_CREATE}} },
+ SCHEMA_MODIFY: func() Transaction { return &SchemaModify{TxBase: TxBase{TransactionType: SCHEMA_MODIFY}} },
+ SCHEMA_DELETE: func() Transaction { return &SchemaDelete{TxBase: TxBase{TransactionType: SCHEMA_DELETE}} },
}
var ledgerEntryNames = [...]string{
@@ -144,6 +152,11 @@ var txNames = [...]string{
CHECK_CANCEL: "CheckCancel",
TABLE_LIST_SET: "TableListSet",
SQLSTATEMENT: "SQLStatement",
+ SQLTRANSACTION: "SQLTransaction",
+ CONTRACT: "Contract",
+ SCHEMA_CREATE: "SchemaCreate",
+ SCHEMA_MODIFY: "SchemaModify",
+ SCHEMA_DELETE: "SchemaDelete",
}
var txTypes = map[string]TransactionType{
@@ -168,6 +181,11 @@ var txTypes = map[string]TransactionType{
"CheckCancel": CHECK_CANCEL,
"TableListSet": TABLE_LIST_SET,
"SQLStatement": SQLSTATEMENT,
+ "SQLTransaction": SQLTRANSACTION,
+ "Contract": CONTRACT,
+ "SchemaCreate": SCHEMA_CREATE,
+ "SchemaModify": SCHEMA_MODIFY,
+ "SchemaDelete": SCHEMA_DELETE,
}
var HashableTypes []string
diff --git a/data/format.go b/data/format.go
index ace0a26..b7a45f5 100644
--- a/data/format.go
+++ b/data/format.go
@@ -95,6 +95,7 @@ var encodings = map[enc]string{
// 16-bit unsigned integers (uncommon)
enc{ST_UINT16, 16}: "Version",
enc{ST_UINT16, 50}: "OpType",
+ enc{ST_UINT16, 51}: "ContractOpType",
// 32-bit unsigned integers (common)
enc{ST_UINT32, 2}: "Flags",
enc{ST_UINT32, 3}: "SourceTag",
@@ -134,6 +135,7 @@ var encodings = map[enc]string{
enc{ST_UINT32, 37}: "FinishAfter",
enc{ST_UINT32, 38}: "SignerListID",
enc{ST_UINT32, 39}: "SettleDelay",
+ enc{ST_UINT32, 55}: "Gas",
// 64-bit unsigned integers (common)
enc{ST_UINT64, 1}: "IndexNext",
enc{ST_UINT64, 2}: "IndexPrevious",
@@ -166,6 +168,8 @@ var encodings = map[enc]string{
enc{ST_HASH256, 21}: "Digest",
enc{ST_HASH256, 22}: "Channel",
enc{ST_HASH256, 24}: "CheckID",
+ enc{ST_HASH256, 58}: "AnchorLedgerHash",
+ enc{ST_HASH256, 59}: "SchemaID",
// currency amount (common)
enc{ST_AMOUNT, 1}: "Amount",
enc{ST_AMOUNT, 2}: "Balance",
@@ -181,6 +185,7 @@ var encodings = map[enc]string{
enc{ST_AMOUNT, 16}: "MinimumOffer",
enc{ST_AMOUNT, 17}: "RippleEscrow",
enc{ST_AMOUNT, 18}: "DeliveredAmount",
+ enc{ST_AMOUNT, 19}: "ContractValue",
// variable length (common)
enc{ST_VL, 1}: "PublicKey",
enc{ST_VL, 2}: "MessageKey",
@@ -203,16 +208,25 @@ var encodings = map[enc]string{
enc{ST_VL, 51}: "TableName",
enc{ST_VL, 52}: "Raw",
enc{ST_VL, 54}: "AutoFillField",
+ enc{ST_VL, 64}: "ContractCode",
+ enc{ST_VL, 65}: "ContractData",
+ enc{ST_VL, 66}: "ContractTxs",
+ enc{ST_VL, 67}: "ContractLogs",
+ enc{ST_VL, 68}: "SchemaName",
+ enc{ST_VL, 69}: "Endpoint",
+ enc{ST_VL, 75}: "ContractDetailMsg",
// account
- enc{ST_ACCOUNT, 1}: "Account",
- enc{ST_ACCOUNT, 2}: "Owner",
- enc{ST_ACCOUNT, 3}: "Destination",
- enc{ST_ACCOUNT, 4}: "Issuer",
- enc{ST_ACCOUNT, 5}: "Authorize",
- enc{ST_ACCOUNT, 6}: "Unauthorize",
- enc{ST_ACCOUNT, 7}: "Target",
- enc{ST_ACCOUNT, 8}: "RegularKey",
-
+ enc{ST_ACCOUNT, 1}: "Account",
+ enc{ST_ACCOUNT, 2}: "Owner",
+ enc{ST_ACCOUNT, 3}: "Destination",
+ enc{ST_ACCOUNT, 4}: "Issuer",
+ enc{ST_ACCOUNT, 5}: "Authorize",
+ enc{ST_ACCOUNT, 6}: "Unauthorize",
+ enc{ST_ACCOUNT, 7}: "Target",
+ enc{ST_ACCOUNT, 8}: "RegularKey",
+ enc{ST_ACCOUNT, 51}: "OriginalAddress",
+ enc{ST_ACCOUNT, 52}: "ContractAddress",
+ enc{ST_ACCOUNT, 53}: "SchemaAdmin",
// inner object
enc{ST_OBJECT, 1}: "EndOfObject",
enc{ST_OBJECT, 2}: "TransactionMetaData",
@@ -229,6 +243,9 @@ var encodings = map[enc]string{
enc{ST_OBJECT, 16}: "Signer",
enc{ST_OBJECT, 18}: "Majority",
enc{ST_OBJECT, 50}: "Table",
+ enc{ST_OBJECT, 52}: "Validator",
+ enc{ST_OBJECT, 53}: "Peer",
+
// array of objects
enc{ST_ARRAY, 1}: "EndOfArray",
enc{ST_ARRAY, 2}: "SigningAccounts",
@@ -242,12 +259,15 @@ var encodings = map[enc]string{
enc{ST_ARRAY, 51}: "Tables",
// array of objects (uncommon)
enc{ST_ARRAY, 16}: "Majorities",
+ enc{ST_ARRAY, 53}: "Validators",
+ enc{ST_ARRAY, 54}: "PeerList",
// 8-bit unsigned integers (common)
enc{ST_UINT8, 1}: "CloseResolution",
enc{ST_UINT8, 2}: "Method",
enc{ST_UINT8, 3}: "TransactionResult",
// 8-bit unsigned integers (uncommon)
enc{ST_UINT8, 16}: "TickSize",
+ enc{ST_UINT8, 28}: "SchemaStrategy",
// 160-bit (common)
enc{ST_HASH160, 1}: "TakerPaysCurrency",
enc{ST_HASH160, 2}: "TakerPaysIssuer",
diff --git a/data/hash.go b/data/hash.go
index 8f3e7af..d7850c8 100644
--- a/data/hash.go
+++ b/data/hash.go
@@ -4,43 +4,43 @@ import (
"bytes"
"encoding/hex"
"fmt"
+ "regexp"
"strings"
+ "github.com/ChainSQL/go-chainsql-api/common"
"github.com/ChainSQL/go-chainsql-api/crypto"
)
-type KeyType int
-
const (
- ECDSA KeyType = 0
- Ed25519 KeyType = 1
+ PUBKEY_LENGTH_GM int = 65
+ PUBKEYL_ENGTH_COMMON int = 33
)
-func (keyType KeyType) String() string {
- switch keyType {
- case ECDSA:
- return "ECDSA"
- case Ed25519:
- return "Ed25519"
- default:
- return "unknown key type"
- }
-}
+
type Hash128 [16]byte
type Hash160 [20]byte
type Hash256 [32]byte
type Vector256 []Hash256
type VariableLength []byte
-type PublicKey [33]byte
+
+//type PublicKey [33]byte
+type PublicKey struct {
+ KeyStore [65]byte
+ KeyValue []byte
+ KeyType common.KeyType
+}
+
type Account [20]byte
type RegularKey [20]byte
-type Seed [16]byte
+
+//type Seed [16]byte
var zero256 Hash256
var zeroAccount Account
-var zeroPublicKey PublicKey
-var zeroSeed Seed
+
+//var zeroPublicKey PublicKey
+//var zeroSeed Seed
func (h *Hash128) Bytes() []byte {
if h == nil {
@@ -159,8 +159,17 @@ func (v *VariableLength) Bytes() []byte {
return []byte(nil)
}
+func (p *PublicKey) SetKey(kType common.KeyType) {
+ p.KeyType = kType
+ if common.SoftGMAlg == kType {
+ p.KeyValue = p.KeyStore[:PUBKEY_LENGTH_GM]
+ } else {
+ p.KeyValue = p.KeyStore[:PUBKEYL_ENGTH_COMMON]
+ }
+
+}
func (p PublicKey) NodePublicKey() string {
- hash, err := crypto.NewNodePublicKey(p[:])
+ hash, err := crypto.NewNodePublicKey(p.KeyValue[:])
if err != nil {
return "Bad node public key"
}
@@ -173,12 +182,15 @@ func (p PublicKey) String() string {
}
func (p PublicKey) IsZero() bool {
- return p == zeroPublicKey
+ if p.KeyValue == nil {
+ return true
+ }
+ return false
}
func (p *PublicKey) Bytes() []byte {
if p != nil {
- return p[:]
+ return p.KeyValue[:]
}
return []byte(nil)
}
@@ -266,63 +278,79 @@ func (r *RegularKey) Bytes() []byte {
}
// Expects address in base58 form
-func NewSeedFromAddress(s string) (*Seed, error) {
- hash, err := crypto.NewRippleHashCheck(s, crypto.RIPPLE_FAMILY_SEED)
- if err != nil {
- return nil, err
- }
- var seed Seed
- copy(seed[:], hash.Payload())
- return &seed, nil
-}
-
-func (s Seed) Hash() (crypto.Hash, error) {
- return crypto.NewFamilySeed(s[:])
-}
-
-func (s Seed) String() string {
- address, err := s.Hash()
- if err != nil {
- return fmt.Sprintf("Bad Address: %s", b2h(s[:]))
- }
- return address.String()
-}
-
-func (s *Seed) Bytes() []byte {
- if s != nil {
- return s[:]
- }
- return []byte(nil)
-}
-
-func (s *Seed) Key(keyType KeyType) crypto.Key {
- var (
- key crypto.Key
- err error
- )
- switch keyType {
- case Ed25519:
- key, err = crypto.NewEd25519Key(s[:])
- case ECDSA:
- key, err = crypto.NewECDSAKey(s[:])
- }
- if err != nil {
- panic(fmt.Sprintf("bad seed: %v", err))
- }
- return key
-}
-
-func (s *Seed) AccountId(keyType KeyType, sequence *uint32) Account {
- var account Account
- copy(account[:], s.Key(keyType).Id(sequence))
- return account
-}
+// func NewSeedFromAddress(s string, version crypto.HashVersion) (*Seed, error) {
+// keySeed := &Seed{}
+// hash, err := crypto.NewRippleHashCheck(s, version)
+// if err != nil {
+// return nil, err
+// }
+// keySeed.seedHash = hash
+// return keySeed, nil
+// }
+
+// func (s *Seed) Hash() (crypto.Hash, error) {
+// return crypto.NewFamilySeed(s[:])
+// }
+
+// func (s Seed) String() string {
+// address, err := s.Hash()
+// if err != nil {
+// return fmt.Sprintf("Bad Address: %s", b2h(s[:]))
+// }
+// return address.String()
+// }
+
+// func (s *Seed) Bytes() []byte {
+// if s != nil {
+// return s[:]
+// }
+// return []byte(nil)
+// }
+
+// func (s *Seed) Key(keyType KeyType) crypto.Key {
+// var (
+// key crypto.Key
+// err error
+// )
+// switch keyType {
+// case Ed25519:
+// key, err = crypto.NewEd25519Key(s[:])
+// case ECDSA:
+// key, err = crypto.NewECDSAKey(s[:])
+// case SoftGMAlg:
+// key, err = crypto.GenerateKeyPairBySeed(s[:])
+// }
+// if err != nil {
+// panic(fmt.Sprintf("bad seed: %v", err))
+// }
+// return key
+// }
+
+// func (s *Seed) AccountId(keyType KeyType, sequence *uint32) Account {
+// var account Account
+// copy(account[:], s.Key(keyType).Id(sequence))
+// return account
+// }
func KeyFromSecret(secret string) (crypto.Key, error) {
- seed, err := NewSeedFromAddress(secret)
- if err != nil {
- return nil, err
+ var version crypto.HashVersion
+ var err error
+ var seed *crypto.Seed
+ var regSoftGMSeed = "^[a-zA-Z1-9]{51,51}"
+ r := regexp.MustCompile(regSoftGMSeed)
+ if r.MatchString(secret) {
+ version = crypto.RIPPLE_ACCOUNT_PRIVATE
+ seed, err = crypto.NewRippleSeed(secret, version)
+ if err != nil {
+ return nil, err
+ }
+ return seed.GenerateKey(common.SoftGMAlg)
+ } else {
+ version = crypto.RIPPLE_FAMILY_SEED
+ seed, err := crypto.NewRippleSeed(secret, version)
+ if err != nil {
+ return nil, err
+ }
+ return seed.GenerateKey(common.ECDSA)
}
-
- return seed.Key(ECDSA), nil
}
diff --git a/data/interface.go b/data/interface.go
index 7f01d00..ff4cc3e 100644
--- a/data/interface.go
+++ b/data/interface.go
@@ -2,6 +2,8 @@ package data
import (
"io"
+
+ "github.com/ChainSQL/go-chainsql-api/common"
)
type Hashable interface {
@@ -12,15 +14,19 @@ type Hashable interface {
type Signer interface {
Hashable
- InitialiseForSigning()
+ InitialiseForSigning(kType common.KeyType)
SigningPrefix() HashPrefix
GetPublicKey() *PublicKey
GetSignature() *VariableLength
+ GetBlob() *VariableLength
+ SetTxBase(seq uint32, fee Value, astLedgerSequence *uint32, account Account)
+ GetRaw() string
+ GetStatements() string
}
type Router interface {
Hashable
- SuppressionId() Hash256
+ SuppressionId(keyType common.KeyType) Hash256
}
type Storer interface {
diff --git a/data/json.go b/data/json.go
index f9da62b..108bb95 100644
--- a/data/json.go
+++ b/data/json.go
@@ -464,23 +464,23 @@ func (r *RegularKey) UnmarshalText(b []byte) error {
return nil
}
-func (s Seed) MarshalText() ([]byte, error) {
- address, err := s.Hash()
- if err != nil {
- return nil, err
- }
- return address.MarshalText()
-}
+// func (s Seed) MarshalText() ([]byte, error) {
+// address, err := s.Hash()
+// if err != nil {
+// return nil, err
+// }
+// return address.MarshalText()
+// }
// Expects base58-encoded account id
-func (s *Seed) UnmarshalText(b []byte) error {
- account, err := NewSeedFromAddress(string(b))
- if err != nil {
- return err
- }
- copy(s[:], account[:])
- return nil
-}
+// func (s *Seed) UnmarshalText(b []byte, version crypto.HashVersion) error {
+// account, err := NewSeedFromAddress(string(b), version)
+// if err != nil {
+// return err
+// }
+// copy(s[:], account[:])
+// return nil
+// }
func (v VariableLength) MarshalText() ([]byte, error) {
return b2h(v), nil
@@ -497,12 +497,12 @@ func (p PublicKey) MarshalText() ([]byte, error) {
if p.IsZero() {
return []byte{}, nil
}
- return b2h(p[:]), nil
+ return b2h(p.KeyValue[:]), nil
}
// Expects public key hex
func (p *PublicKey) UnmarshalText(b []byte) error {
- _, err := hex.Decode(p[:], b)
+ _, err := hex.Decode(p.KeyValue[:], b)
return err
}
@@ -518,6 +518,4 @@ func (h *Uint64Hex) UnmarshalText(b []byte) error {
return err
}
-func (keyType KeyType) MarshalText() ([]byte, error) {
- return []byte(keyType.String()), nil
-}
+
diff --git a/data/log.go b/data/log.go
new file mode 100644
index 0000000..5899812
--- /dev/null
+++ b/data/log.go
@@ -0,0 +1,139 @@
+// 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 data
+
+import (
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/common/hexutil"
+ // "github.com/ethereum/go-ethereum/rlp"
+)
+
+//go:generate go run github.com/fjl/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"`
+ Address string `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"`
+ // 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"`
+
+ // 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
+}
+
+//go:generate go run ../../rlp/rlpgen -type rlpLog -out gen_log_rlp.go
+
+type rlpLog struct {
+ Address common.Address
+ Topics []common.Hash
+ Data []byte
+}
+
+// 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 {
+// rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
+// return rlp.Encode(w, &rl)
+// }
+
+// // 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 handles
+// // backward compatibility with prior storage formats.
+// type LogForStorage Log
+
+// // EncodeRLP implements rlp.Encoder.
+// func (l *LogForStorage) EncodeRLP(w io.Writer) error {
+// rl := rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}
+// return rlp.Encode(w, &rl)
+// }
+
+// // 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 rlpLog
+// 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/data/metadata.go b/data/metadata.go
index 27b4e82..29b91bd 100644
--- a/data/metadata.go
+++ b/data/metadata.go
@@ -65,13 +65,17 @@ func (t *TransactionWithMetaData) NodeType() NodeType { return NT_TRANSACTION_NO
func (t *TransactionWithMetaData) Ledger() uint32 { return t.LedgerSequence }
func (t *TransactionWithMetaData) NodeId() *Hash256 { return &t.Id }
-func (t *TransactionWithMetaData) Affects(account Account) bool {
+func (t *TransactionWithMetaData) Affects(account Account) (bool, error) {
for _, effect := range t.MetaData.AffectedNodes {
- if _, final, _, _ := effect.AffectedNode(); final.Affects(account) {
- return true
+ _, final, _, _ , err:= effect.AffectedNode()
+ if err != nil{
+ return false,err
+ }
+ if final.Affects(account) {
+ return true, nil
}
}
- return false
+ return false, nil
}
func NewTransactionWithMetadata(typ TransactionType) *TransactionWithMetaData {
@@ -80,7 +84,7 @@ func NewTransactionWithMetadata(typ TransactionType) *TransactionWithMetaData {
// AffectedNode returns the AffectedNode, the current LedgerEntry,
// the previous LedgerEntry (which might be nil) and the LedgerEntryState
-func (effect *NodeEffect) AffectedNode() (*AffectedNode, LedgerEntry, LedgerEntry, LedgerEntryState) {
+func (effect *NodeEffect) AffectedNode() (*AffectedNode, LedgerEntry, LedgerEntry, LedgerEntryState, error) {
var (
node *AffectedNode
final, previous LedgerEntry
@@ -96,11 +100,11 @@ func (effect *NodeEffect) AffectedNode() (*AffectedNode, LedgerEntry, LedgerEntr
case effect.ModifiedNode != nil && effect.ModifiedNode.FinalFields == nil:
node, final, state = effect.ModifiedNode, LedgerEntryFactory[effect.ModifiedNode.LedgerEntryType](), Modified
default:
- panic(fmt.Sprintf("Unknown LedgerEntryState: %+v", effect))
+ return nil, nil, nil, Created, fmt.Errorf("Unknown LedgerEntryState: %+v", effect)
}
previous = node.PreviousFields
if previous == nil {
previous = LedgerEntryFactory[final.GetLedgerEntryType()]()
}
- return node, final, previous, state
+ return node, final, previous, state, nil
}
diff --git a/data/orderbook.go b/data/orderbook.go
index 4527711..d97309c 100644
--- a/data/orderbook.go
+++ b/data/orderbook.go
@@ -97,10 +97,10 @@ func defaultUint32(v *uint32) uint32 {
return *v
}
-func (s *AccountOfferSlice) Add(offer *Offer) bool {
+func (s *AccountOfferSlice) Add(offer *Offer) (bool, error) {
quality, err := offer.TakerPays.Divide(offer.TakerGets)
if err != nil {
- panic(fmt.Sprintf("impossible quality: %s %s", offer.TakerPays, offer.TakerGets))
+ return false, fmt.Errorf("impossible quality: %s %s", offer.TakerPays, offer.TakerGets)
}
o := AccountOffer{
Flags: LedgerEntryFlag(defaultUint32((*uint32)(offer.Flags))),
@@ -114,14 +114,14 @@ func (s *AccountOfferSlice) Add(offer *Offer) bool {
switch {
case i == len(*s):
*s = append(*s, o)
- return true
+ return true, nil
case (*s)[i].Sequence != *offer.Sequence:
*s = append(*s, AccountOffer{})
copy((*s)[i+1:], (*s)[i:])
(*s)[i] = o
- return true
+ return true, nil
default:
- return false
+ return false, nil
}
}
diff --git a/data/proposal.go b/data/proposal.go
index c299d59..3db5cac 100644
--- a/data/proposal.go
+++ b/data/proposal.go
@@ -1,5 +1,7 @@
package data
+import "github.com/ChainSQL/go-chainsql-api/common"
+
type Proposal struct {
Hash Hash256
LedgerHash Hash256
@@ -16,7 +18,7 @@ func (p *Proposal) GetSignature() *VariableLength { return &p.Signature }
func (p *Proposal) Prefix() HashPrefix { return HP_PROPOSAL }
func (p *Proposal) SigningPrefix() HashPrefix { return HP_PROPOSAL }
func (p *Proposal) GetHash() *Hash256 { return &p.Hash }
-func (p *Proposal) InitialiseForSigning() {}
+func (p *Proposal) InitialiseForSigning(kType common.KeyType) {}
func (p Proposal) SigningValues() []interface{} {
return []interface{}{
@@ -27,7 +29,7 @@ func (p Proposal) SigningValues() []interface{} {
}
}
-func (p Proposal) SuppressionId() (Hash256, error) {
+func (p Proposal) SuppressionId(keyType common.KeyType) (Hash256, error) {
return hashValues([]interface{}{
p.LedgerHash,
p.PreviousLedger,
diff --git a/data/schema.go b/data/schema.go
new file mode 100644
index 0000000..03ea581
--- /dev/null
+++ b/data/schema.go
@@ -0,0 +1,45 @@
+package data
+
+type CreateSchema struct {
+ SchemaName string
+ WithState bool
+ SchemaAdmin string `json:"SchemaAdmin,omitempty"`
+ AnchorLedgerHash string `json:"AnchorLedgerHash,omitempty"`
+ PeerList []Peer
+ Validators []Validator
+}
+
+type ModifySchema struct {
+ OpType uint16
+ Validators []Validator
+ PeerList []Peer
+ SchemaID string
+}
+
+type Peer struct {
+ Peer Endpoint
+}
+type PeerFormat struct {
+ Peer EndpointFormat
+}
+type Endpoint struct {
+ Endpoint string
+}
+
+type EndpointFormat struct {
+ Endpoint VariableLength
+}
+
+type Validator struct {
+ Validator PublicKeyObj
+}
+type ValidatorFormat struct {
+ Validator PublicKeyObjFormat
+}
+type PublicKeyObj struct {
+ PublicKey string
+}
+
+type PublicKeyObjFormat struct {
+ PublicKey VariableLength
+}
diff --git a/data/signing.go b/data/signing.go
index 7a641a6..48080d3 100644
--- a/data/signing.go
+++ b/data/signing.go
@@ -1,29 +1,33 @@
package data
-import "github.com/ChainSQL/go-chainsql-api/crypto"
+import (
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/crypto"
+)
-func Sign(s Signer, key crypto.Key, sequence *uint32) error {
- s.InitialiseForSigning()
+func Sign(s Signer, key crypto.Key, sequence *uint32, keyType common.KeyType) error {
+ s.InitialiseForSigning(key.Type())
copy(s.GetPublicKey().Bytes(), key.Public(sequence))
- hash, msg, err := SigningHash(s)
+ hash, msg, err := SigningHash(s, keyType)
if err != nil {
return err
}
- sig, err := crypto.Sign(key.Private(sequence), hash.Bytes(), append(s.SigningPrefix().Bytes(), msg...))
+ sig, err := crypto.Sign(key, hash.Bytes(), sequence, append(s.SigningPrefix().Bytes(), msg...))
if err != nil {
return err
}
*s.GetSignature() = VariableLength(sig)
- hash, _, err = Raw(s)
+ hash, blob, err := Raw(s, keyType)
if err != nil {
return err
}
copy(s.GetHash().Bytes(), hash.Bytes())
+ *s.GetBlob() = VariableLength(blob)
return nil
}
-func CheckSignature(s Signer) (bool, error) {
- hash, msg, err := SigningHash(s)
+func CheckSignature(s Signer, keyType common.KeyType) (bool, error) {
+ hash, msg, err := SigningHash(s, keyType)
if err != nil {
return false, err
}
diff --git a/data/trade.go b/data/trade.go
index 2852242..2466aa8 100644
--- a/data/trade.go
+++ b/data/trade.go
@@ -17,7 +17,10 @@ type Trade struct {
}
func newTrade(txm *TransactionWithMetaData, i int) (*Trade, error) {
- _, final, previous, action := txm.MetaData.AffectedNodes[i].AffectedNode()
+ _, final, previous, action, err := txm.MetaData.AffectedNodes[i].AffectedNode()
+ if err != nil {
+ return nil, err
+ }
v, ok := final.(*Offer)
if !ok || action == Created {
return nil, nil
diff --git a/data/transaction.go b/data/transaction.go
index db6a509..a41aa71 100644
--- a/data/transaction.go
+++ b/data/transaction.go
@@ -1,5 +1,7 @@
package data
+import "github.com/ChainSQL/go-chainsql-api/common"
+
type TxBase struct {
TransactionType TransactionType
Flags *TransactionFlag `json:",omitempty"`
@@ -14,6 +16,7 @@ type TxBase struct {
PreviousTxnID *Hash256 `json:",omitempty"`
LastLedgerSequence *uint32 `json:",omitempty"`
Hash Hash256 `json:"hash"`
+ TxBlob *VariableLength
}
type Payment struct {
@@ -194,16 +197,57 @@ type SQLStatement struct {
// AutoFillField *VariableLength `json:"AutoFillField,omitempty"`
}
+type SchemaCreate struct {
+ TxBase
+ SchemaName VariableLength
+ SchemaStrategy uint8
+ SchemaAdmin *Account `json:",omitempty"`
+ AnchorLedgerHash *Hash256 `json:",omitempty"`
+ Validators []ValidatorFormat
+ PeerList []PeerFormat
+}
+
+type SchemaModify struct {
+ TxBase
+ OpType uint16
+ Validators []ValidatorFormat
+ PeerList []PeerFormat
+ SchemaID Hash256
+}
+
+type SchemaDelete struct {
+ TxBase
+ SchemaID Hash256
+}
+
+type ContractTx struct {
+ TxBase
+ ContractAddress Account `json:"ContractAddress"` //option
+ ContractOpType uint16 `json:"ContractOpType"`
+ ContractData VariableLength `json:"ContractData"`
+ ContractValue Amount `json:"ContractValue"`
+ Gas uint32 `json:"Gas"`
+}
+
+func (txBase *TxBase) SetTxBase(seq uint32, fee Value, lastLedgerSequence *uint32, account Account) {
+ txBase.Sequence = seq
+ txBase.Fee = fee
+ txBase.Account = account
+ txBase.LastLedgerSequence = lastLedgerSequence
+}
+
func (t *TxBase) GetBase() *TxBase { return t }
func (t *TxBase) GetType() string { return txNames[t.TransactionType] }
func (t *TxBase) GetTransactionType() TransactionType { return t.TransactionType }
func (t *TxBase) Prefix() HashPrefix { return HP_TRANSACTION_ID }
func (t *TxBase) GetPublicKey() *PublicKey { return t.SigningPubKey }
func (t *TxBase) GetSignature() *VariableLength { return t.TxnSignature }
+func (t *TxBase) GetBlob() *VariableLength { return t.TxBlob }
func (t *TxBase) SigningPrefix() HashPrefix { return HP_TRANSACTION_SIGN }
func (t *TxBase) PathSet() PathSet { return PathSet(nil) }
func (t *TxBase) GetHash() *Hash256 { return &t.Hash }
-
+func (t *TxBase) GetRaw() string { return "" }
+func (t *TxBase) GetStatements() string { return "" }
func (t *TxBase) Compare(other *TxBase) int {
switch {
case t.Account.Equals(other.Account):
@@ -222,13 +266,22 @@ func (t *TxBase) Compare(other *TxBase) int {
}
}
-func (t *TxBase) InitialiseForSigning() {
+// func SetBase(txInterface TxInterface) {
+// txInterface.SetSeq()
+
+// }
+func (t *TxBase) InitialiseForSigning(kType common.KeyType) {
if t.SigningPubKey == nil {
- t.SigningPubKey = new(PublicKey)
+ pubkey := &PublicKey{}
+ pubkey.SetKey(kType)
+ t.SigningPubKey = pubkey
}
if t.TxnSignature == nil {
t.TxnSignature = new(VariableLength)
}
+ if t.TxBlob == nil {
+ t.TxBlob = new(VariableLength)
+ }
}
func (o *OfferCreate) Ratio() *Value {
diff --git a/data/validation.go b/data/validation.go
index 1ad51a3..d541c43 100644
--- a/data/validation.go
+++ b/data/validation.go
@@ -1,5 +1,9 @@
package data
+import (
+ "github.com/ChainSQL/go-chainsql-api/common"
+)
+
type Validation struct {
Hash Hash256
Flags uint32
@@ -21,6 +25,6 @@ func (v Validation) GetPublicKey() *PublicKey { return &v.SigningPubKey }
func (v Validation) GetSignature() *VariableLength { return &v.Signature }
func (v Validation) Prefix() HashPrefix { return HP_VALIDATION }
func (v Validation) SigningPrefix() HashPrefix { return HP_VALIDATION }
-func (v Validation) SuppressionId() (Hash256, error) { return NodeId(&v) }
+func (v Validation) SuppressionId(keyType common.KeyType) (Hash256, error) { return NodeId(&v, keyType) }
func (v Validation) GetHash() *Hash256 { return &v.Hash }
-func (v Validation) InitialiseForSigning() {}
+func (v Validation) InitialiseForSigning(kType common.KeyType) {}
diff --git a/data/wire.go b/data/wire.go
index b1df792..2489b54 100644
--- a/data/wire.go
+++ b/data/wire.go
@@ -140,7 +140,7 @@ func (a *Account) Marshal(w io.Writer) error {
}
func (k *PublicKey) Unmarshal(r Reader) error {
- return readExpectedLength(r, k[:], "PublicKey")
+ return readExpectedLength(r, k.KeyValue[:], "PublicKey")
}
func (k *PublicKey) Marshal(w io.Writer) error {
diff --git a/event/manager.go b/event/manager.go
index be29d98..879d899 100644
--- a/event/manager.go
+++ b/event/manager.go
@@ -1,9 +1,13 @@
package event
import (
+ "encoding/hex"
"log"
+ "strings"
"sync"
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/data"
"github.com/ChainSQL/go-chainsql-api/export"
"github.com/ChainSQL/go-chainsql-api/util"
"github.com/buger/jsonparser"
@@ -12,6 +16,8 @@ import (
// Manager manages the subscription
type Manager struct {
txCache map[string]export.Callback
+ contractCache map[string]bool
+ ctrEventCache map[string]chan *data.Log
tableCache map[string]export.Callback
ledgerCloseCache []export.Callback
muxTx *sync.Mutex
@@ -23,6 +29,8 @@ func NewEventManager() *Manager {
return &Manager{
txCache: make(map[string]export.Callback),
tableCache: make(map[string]export.Callback),
+ contractCache: make(map[string]bool),
+ ctrEventCache: make(map[string]chan *data.Log),
ledgerCloseCache: make([]export.Callback, 0, 10),
muxTx: new(sync.Mutex),
muxTable: new(sync.Mutex),
@@ -57,6 +65,32 @@ func (e *Manager) UnSubscribeTx(hash string) {
e.muxTx.Unlock()
}
+//SubscribeCtrAddr subscribe a contract
+func (e *Manager) SubscribeCtrAddr(address string, ok bool) {
+ e.muxTx.Lock()
+ e.contractCache[address] = ok
+ e.muxTx.Unlock()
+}
+
+//UnSubscribeCtrAddr unsubscribe a contract
+func (e *Manager) UnSubscribeCtrAddr(address string) {
+ e.muxTx.Lock()
+ delete(e.contractCache, address)
+ e.muxTx.Unlock()
+}
+
+func (e *Manager) RegisterCtrEvent(eventSign string, contractMsgCh chan *data.Log) {
+ e.muxTx.Lock()
+ e.ctrEventCache[strings.ToUpper(eventSign[2:])] = contractMsgCh
+ e.muxTx.Unlock()
+}
+
+func (e *Manager) UnRegisterCtrEvent(eventSign string, contractMsgCh chan *data.Log) {
+ e.muxTx.Lock()
+ delete(e.ctrEventCache, eventSign)
+ e.muxTx.Unlock()
+}
+
// SubscribeLedger subscribe ledgerClosed
func (e *Manager) SubscribeLedger(callback export.Callback) {
e.ledgerCloseCache = append(e.ledgerCloseCache, callback)
@@ -69,6 +103,70 @@ func (e *Manager) OnLedgerClosed(msg string) {
}
}
+func (e *Manager) OnContractMsg(msg string) {
+ log.Println(msg)
+ ctrEventTopic, err := jsonparser.GetString([]byte(msg), "ContractEventTopics", "[0]")
+ if err != nil {
+ return
+ }
+
+ logRaw := &data.Log{}
+ contractAddr, err := jsonparser.GetString([]byte(msg), "ContractAddress")
+ if err != nil {
+ return
+ }
+ logRaw.Address = contractAddr
+ ctrEventInfo, err := jsonparser.GetString([]byte(msg), "ContractEventInfo")
+ if err != nil {
+ return
+ }
+ ctrEventInfoHex, err := hex.DecodeString(ctrEventInfo)
+ if err != nil {
+ return
+ }
+ logRaw.Data = ctrEventInfoHex
+ ctrEventTopics, _, _, err := jsonparser.Get([]byte(msg), "ContractEventTopics")
+ if err != nil {
+ return
+ }
+ _, _ = jsonparser.ArrayEach(ctrEventTopics, func(value []byte, dataType jsonparser.ValueType, offset int, errin error) {
+ valueStr := string(value)
+ valueHex, err := hex.DecodeString(valueStr)
+ if err != nil {
+ return
+ }
+ logRaw.Topics = append(logRaw.Topics, common.BytesToHash(valueHex))
+ })
+
+ e.muxTx.Lock()
+ if _, ok := e.contractCache[contractAddr]; ok {
+ if eventMsgCh, ok := e.ctrEventCache[ctrEventTopic]; ok {
+ eventMsgCh <- logRaw
+ }
+ }
+ e.muxTx.Unlock()
+ // if (data.hasOwnProperty("ContractEventTopics")) {
+ // data.ContractEventTopics.map(function (topic, index) {
+ // data.ContractEventTopics[index] = "0x" + data.ContractEventTopics[index].toLowerCase();
+ // });
+ // }
+ // if (data.hasOwnProperty("ContractEventInfo") && data.ContractEventInfo !== "") {
+ // data.ContractEventInfo = "0x" + data.ContractEventInfo;
+ // }
+ // let key = data.ContractEventTopics[0];
+ // if (that.cache[key]) {
+ // let contractObj = that.cache[data.ContractAddress];
+ // let currentEvent = contractObj.options.jsonInterface.find(function (json) {
+ // return (json.type === 'event' && json.signature === '0x' + key.replace('0x', ''));
+ // });
+ // let output = contractObj._decodeEventABI(currentEvent, data);
+ // that.cache[key](null, output);
+ // // delete that.cache[key];
+ // // let keyIndex = contractObj.registeredEvent.indexOf(key);
+ // // contractObj.registeredEvent.splice(keyIndex,1);
+ // }
+}
+
// OnSingleTransaction trigger the callback
func (e *Manager) OnSingleTransaction(msg string) {
// log.Println(msg)
diff --git a/event/subscription.go b/event/subscription.go
new file mode 100644
index 0000000..2c8484a
--- /dev/null
+++ b/event/subscription.go
@@ -0,0 +1,295 @@
+// Copyright 2016 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 event
+
+import (
+ "sync"
+ // "github.com/ethereum/go-ethereum/common/mclock"
+)
+
+// Subscription represents a stream of events. The carrier of the events is typically a
+// channel, but isn't part of the interface.
+//
+// Subscriptions can fail while established. Failures are reported through an error
+// channel. It receives a value if there is an issue with the subscription (e.g. the
+// network connection delivering the events has been closed). Only one value will ever be
+// sent.
+//
+// The error channel is closed when the subscription ends successfully (i.e. when the
+// source of events is closed). It is also closed when Unsubscribe is called.
+//
+// The Unsubscribe method cancels the sending of events. You must call Unsubscribe in all
+// cases to ensure that resources related to the subscription are released. It can be
+// called any number of times.
+type Subscription interface {
+ Err() <-chan error // returns the error channel
+ Unsubscribe() // cancels sending of events, closing the error channel
+}
+
+// NewSubscription runs a producer function as a subscription in a new goroutine. The
+// channel given to the producer is closed when Unsubscribe is called. If fn returns an
+// error, it is sent on the subscription's error channel.
+func NewSubscription(producer func(<-chan struct{}) error) Subscription {
+ s := &funcSub{unsub: make(chan struct{}), err: make(chan error, 1)}
+ go func() {
+ defer close(s.err)
+ err := producer(s.unsub)
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if !s.unsubscribed {
+ if err != nil {
+ s.err <- err
+ }
+ s.unsubscribed = true
+ }
+ }()
+ return s
+}
+
+type funcSub struct {
+ unsub chan struct{}
+ err chan error
+ mu sync.Mutex
+ unsubscribed bool
+}
+
+func (s *funcSub) Unsubscribe() {
+ s.mu.Lock()
+ if s.unsubscribed {
+ s.mu.Unlock()
+ return
+ }
+ s.unsubscribed = true
+ close(s.unsub)
+ s.mu.Unlock()
+ // Wait for producer shutdown.
+ <-s.err
+}
+
+func (s *funcSub) Err() <-chan error {
+ return s.err
+}
+
+// // Resubscribe calls fn repeatedly to keep a subscription established. When the
+// // subscription is established, Resubscribe waits for it to fail and calls fn again. This
+// // process repeats until Unsubscribe is called or the active subscription ends
+// // successfully.
+// //
+// // Resubscribe applies backoff between calls to fn. The time between calls is adapted
+// // based on the error rate, but will never exceed backoffMax.
+// func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription {
+// return ResubscribeErr(backoffMax, func(ctx context.Context, _ error) (Subscription, error) {
+// return fn(ctx)
+// })
+// }
+
+// // A ResubscribeFunc attempts to establish a subscription.
+// type ResubscribeFunc func(context.Context) (Subscription, error)
+
+// // ResubscribeErr calls fn repeatedly to keep a subscription established. When the
+// // subscription is established, ResubscribeErr waits for it to fail and calls fn again. This
+// // process repeats until Unsubscribe is called or the active subscription ends
+// // successfully.
+// //
+// // The difference between Resubscribe and ResubscribeErr is that with ResubscribeErr,
+// // the error of the failing subscription is available to the callback for logging
+// // purposes.
+// //
+// // ResubscribeErr applies backoff between calls to fn. The time between calls is adapted
+// // based on the error rate, but will never exceed backoffMax.
+// func ResubscribeErr(backoffMax time.Duration, fn ResubscribeErrFunc) Subscription {
+// s := &resubscribeSub{
+// waitTime: backoffMax / 10,
+// backoffMax: backoffMax,
+// fn: fn,
+// err: make(chan error),
+// unsub: make(chan struct{}),
+// }
+// go s.loop()
+// return s
+// }
+
+// // A ResubscribeErrFunc attempts to establish a subscription.
+// // For every call but the first, the second argument to this function is
+// // the error that occurred with the previous subscription.
+// type ResubscribeErrFunc func(context.Context, error) (Subscription, error)
+
+// type resubscribeSub struct {
+// fn ResubscribeErrFunc
+// err chan error
+// unsub chan struct{}
+// unsubOnce sync.Once
+// lastTry mclock.AbsTime
+// lastSubErr error
+// waitTime, backoffMax time.Duration
+// }
+
+// func (s *resubscribeSub) Unsubscribe() {
+// s.unsubOnce.Do(func() {
+// s.unsub <- struct{}{}
+// <-s.err
+// })
+// }
+
+// func (s *resubscribeSub) Err() <-chan error {
+// return s.err
+// }
+
+// func (s *resubscribeSub) loop() {
+// defer close(s.err)
+// var done bool
+// for !done {
+// sub := s.subscribe()
+// if sub == nil {
+// break
+// }
+// done = s.waitForError(sub)
+// sub.Unsubscribe()
+// }
+// }
+
+// func (s *resubscribeSub) subscribe() Subscription {
+// subscribed := make(chan error)
+// var sub Subscription
+// for {
+// s.lastTry = mclock.Now()
+// ctx, cancel := context.WithCancel(context.Background())
+// go func() {
+// rsub, err := s.fn(ctx, s.lastSubErr)
+// sub = rsub
+// subscribed <- err
+// }()
+// select {
+// case err := <-subscribed:
+// cancel()
+// if err == nil {
+// if sub == nil {
+// panic("event: ResubscribeFunc returned nil subscription and no error")
+// }
+// return sub
+// }
+// // Subscribing failed, wait before launching the next try.
+// if s.backoffWait() {
+// return nil // unsubscribed during wait
+// }
+// case <-s.unsub:
+// cancel()
+// <-subscribed // avoid leaking the s.fn goroutine.
+// return nil
+// }
+// }
+// }
+
+// func (s *resubscribeSub) waitForError(sub Subscription) bool {
+// defer sub.Unsubscribe()
+// select {
+// case err := <-sub.Err():
+// s.lastSubErr = err
+// return err == nil
+// case <-s.unsub:
+// return true
+// }
+// }
+
+// func (s *resubscribeSub) backoffWait() bool {
+// if time.Duration(mclock.Now()-s.lastTry) > s.backoffMax {
+// s.waitTime = s.backoffMax / 10
+// } else {
+// s.waitTime *= 2
+// if s.waitTime > s.backoffMax {
+// s.waitTime = s.backoffMax
+// }
+// }
+
+// t := time.NewTimer(s.waitTime)
+// defer t.Stop()
+// select {
+// case <-t.C:
+// return false
+// case <-s.unsub:
+// return true
+// }
+// }
+
+// // SubscriptionScope provides a facility to unsubscribe multiple subscriptions at once.
+// //
+// // For code that handle more than one subscription, a scope can be used to conveniently
+// // unsubscribe all of them with a single call. The example demonstrates a typical use in a
+// // larger program.
+// //
+// // The zero value is ready to use.
+// type SubscriptionScope struct {
+// mu sync.Mutex
+// subs map[*scopeSub]struct{}
+// closed bool
+// }
+
+// type scopeSub struct {
+// sc *SubscriptionScope
+// s Subscription
+// }
+
+// // Track starts tracking a subscription. If the scope is closed, Track returns nil. The
+// // returned subscription is a wrapper. Unsubscribing the wrapper removes it from the
+// // scope.
+// func (sc *SubscriptionScope) Track(s Subscription) Subscription {
+// sc.mu.Lock()
+// defer sc.mu.Unlock()
+// if sc.closed {
+// return nil
+// }
+// if sc.subs == nil {
+// sc.subs = make(map[*scopeSub]struct{})
+// }
+// ss := &scopeSub{sc, s}
+// sc.subs[ss] = struct{}{}
+// return ss
+// }
+
+// // Close calls Unsubscribe on all tracked subscriptions and prevents further additions to
+// // the tracked set. Calls to Track after Close return nil.
+// func (sc *SubscriptionScope) Close() {
+// sc.mu.Lock()
+// defer sc.mu.Unlock()
+// if sc.closed {
+// return
+// }
+// sc.closed = true
+// for s := range sc.subs {
+// s.s.Unsubscribe()
+// }
+// sc.subs = nil
+// }
+
+// // Count returns the number of tracked subscriptions.
+// // It is meant to be used for debugging.
+// func (sc *SubscriptionScope) Count() int {
+// sc.mu.Lock()
+// defer sc.mu.Unlock()
+// return len(sc.subs)
+// }
+
+// func (s *scopeSub) Unsubscribe() {
+// s.s.Unsubscribe()
+// s.sc.mu.Lock()
+// defer s.sc.mu.Unlock()
+// delete(s.sc.subs, s)
+// }
+
+// func (s *scopeSub) Err() <-chan error {
+// return s.s.Err()
+// }
diff --git a/go.mod b/go.mod
index 16a4492..f7560b7 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,7 @@ require (
github.com/buger/jsonparser v1.1.1
github.com/gorilla/websocket v1.4.2
github.com/kr/text v0.2.0 // indirect
- golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
+ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
+ gopkg.in/urfave/cli.v1 v1.20.0
)
diff --git a/go.sum b/go.sum
deleted file mode 100644
index bbe8234..0000000
--- a/go.sum
+++ /dev/null
@@ -1,60 +0,0 @@
-github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
-github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
-github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
-github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
-github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
-github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
-github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
-github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
-github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
-github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
-github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
-github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
-github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
-github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
-github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
-github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
-github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
-github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
-golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/net/client.go b/net/client.go
index 099c4e5..96fdced 100644
--- a/net/client.go
+++ b/net/client.go
@@ -4,11 +4,13 @@ import (
"encoding/json"
"fmt"
"log"
+ "strings"
"sync"
"time"
"github.com/ChainSQL/go-chainsql-api/common"
"github.com/ChainSQL/go-chainsql-api/crypto"
+ "github.com/ChainSQL/go-chainsql-api/data"
"github.com/ChainSQL/go-chainsql-api/event"
"github.com/ChainSQL/go-chainsql-api/export"
"github.com/ChainSQL/go-chainsql-api/util"
@@ -22,7 +24,7 @@ const ReconnectInterval = 10
// Client is used to send and recv websocket msg
type Client struct {
cmdIDs int64
- schemaID string
+ SchemaID string
wm *WebsocketManager
sendMsgChan chan string
recvMsgChan chan string
@@ -44,16 +46,17 @@ func NewClient() *Client {
ServerInfo: NewServerInfo(),
Event: event.NewEventManager(),
inited: false,
+ SchemaID: "",
}
}
//Connect is used to create a websocket connection
-func (c *Client) Connect(url string) error {
+func (c *Client) Connect(url, tlsRootCertPath, tlsClientCertPath, tlsClientKeyPath, serverName string) error {
if c.wm != nil {
return c.reConnect(url)
}
- c.wm = NewWsClientManager(url, ReconnectInterval)
+ c.wm = NewWsClientManager(url, tlsRootCertPath, tlsClientCertPath, tlsClientKeyPath, serverName, ReconnectInterval)
err := c.wm.Start()
if err != nil {
return err
@@ -77,7 +80,7 @@ func (c *Client) reConnect(url string) error {
c.init()
} else {
//connect changed,only subscribe
- c.initSubscription()
+ c.InitSubscription()
}
return nil
}
@@ -88,13 +91,13 @@ func (c *Client) init() {
go c.processMessage()
go c.checkReconnection()
- c.initSubscription()
+ c.InitSubscription()
c.inited = true
}
func (c *Client) checkReconnection() {
c.wm.OnReconnected(func() {
- c.initSubscription()
+ c.InitSubscription()
})
}
@@ -102,16 +105,16 @@ func (c *Client) GetWebocketManager() *WebsocketManager {
return c.wm
}
-func (c *Client) initSubscription() {
+func (c *Client) InitSubscription() {
type Subscribe struct {
common.RequestBase
Streams []string `json:"streams"`
}
- c.cmdIDs++
+
subCmd := &Subscribe{
RequestBase: common.RequestBase{
Command: "subscribe",
- ID: c.cmdIDs,
+ // ID: c.cmdIDs,
},
Streams: []string{"ledger", "server"},
}
@@ -134,6 +137,7 @@ func (c *Client) processMessage() {
func (c *Client) handleClientMsg(msg string) {
// log.Printf("handleClientMsg: %s", msg)
msgType, err := jsonparser.GetString([]byte(msg), "type")
+ // log.Println(msgType)
if err != nil {
fmt.Printf("handleClientMsg error:%s\n", err)
}
@@ -151,6 +155,8 @@ func (c *Client) handleClientMsg(msg string) {
c.onSingleTransaction(msg)
case "table":
c.onTableMsg(msg)
+ case "contract_event":
+ c.onContractMsg(msg)
default:
log.Printf("Unhandled message %s", msg)
}
@@ -192,17 +198,21 @@ func (c *Client) onTableMsg(msg string) {
c.Event.OnTableMsg(msg)
}
+func (c *Client) onContractMsg(msg string) {
+ c.Event.OnContractMsg(msg)
+}
+
// GetLedger request for ledger data
func (c *Client) GetLedger(seq int) string {
type getLedger struct {
common.RequestBase
LedgerIndex int `json:"ledger_index"`
}
- c.cmdIDs++
+
ledgerReq := &getLedger{
RequestBase: common.RequestBase{
Command: "ledger",
- ID: c.cmdIDs,
+ // ID: c.cmdIDs,
},
LedgerIndex: seq,
}
@@ -211,22 +221,71 @@ func (c *Client) GetLedger(seq int) string {
return request.Response.Value
}
+// GetLedger request for ledger data
+func (c *Client) GetLedgerTransactions(seq int, expand bool) string {
+ type getLedgerTxs struct {
+ common.RequestBase
+ LedgerIndex int `json:"ledger_index"`
+ Transactions bool `json:"transactions"`
+ Expand bool `json:"expand"`
+ }
+ ledgerReq := &getLedgerTxs{
+ RequestBase: common.RequestBase{
+ Command: "ledger",
+ },
+ LedgerIndex: seq,
+ Transactions: true,
+ }
+ if expand {
+ ledgerReq.Expand = true
+ }
+
+ request := c.syncRequest(ledgerReq)
+
+ return request.Response.Value
+}
+
// GetLedgerVersion request for ledger version
func (c *Client) GetLedgerVersion() (int, error) {
+ type ledgerVersionRequest struct {
+ common.RequestBase
+ LedgerIndex string `json:"ledger_index"`
+ }
+ ledgerReq := &ledgerVersionRequest{
+ RequestBase: common.RequestBase{
+ Command: "ledger",
+ },
+ LedgerIndex: "validated",
+ }
+ request := c.syncRequest(ledgerReq)
+ err := c.parseResponseError(request)
+ if err != nil {
+ log.Println("GetLedgerVersion:", err)
+ return 0, err
+ }
+ ledgerIndex, err := jsonparser.GetInt([]byte(request.Response.Value), "result", "ledger_index")
+ if err != nil {
+ return 0, err
+ }
+ return int(ledgerIndex), nil
+}
+
+// GetLedgerCurrent request for ledger version
+func (c *Client) GetLedgerCurrent() (int, error) {
type Request struct {
common.RequestBase
}
- c.cmdIDs++
+
ledgerReq := &Request{
RequestBase: common.RequestBase{
Command: "ledger_current",
- ID: c.cmdIDs,
+ // ID: c.cmdIDs,
},
}
request := c.syncRequest(ledgerReq)
err := c.parseResponseError(request)
if err != nil {
- log.Println("GetLedgerVersion:", err)
+ log.Println("GetLedgerCurrent:", err)
return 0, err
}
ledgerIndex, err := jsonparser.GetInt([]byte(request.Response.Value), "result", "ledger_current_index")
@@ -236,6 +295,10 @@ func (c *Client) GetLedgerVersion() (int, error) {
return int(ledgerIndex), nil
}
+func (c *Client) ParseResponseError(request *Request) error {
+ return c.parseResponseError(request)
+}
+
func (c *Client) parseResponseError(request *Request) error {
status, err := jsonparser.GetString([]byte(request.Response.Value), "status")
if err != nil {
@@ -254,9 +317,8 @@ func (c *Client) GetAccountInfo(address string) (string, error) {
common.RequestBase
Account string `json:"account"`
}
- c.cmdIDs++
+
accountReq := &getAccount{}
- accountReq.ID = c.cmdIDs
accountReq.Command = "account_info"
accountReq.Account = address
@@ -277,9 +339,8 @@ func (c *Client) GetNameInDB(address string, tableName string) (string, error) {
Account string `json:"account"`
TableName string `json:"tablename"`
}
- c.cmdIDs++
+
req := &Request{}
- req.ID = c.cmdIDs
req.Command = "g_dbname"
req.Account = address
req.TableName = tableName
@@ -303,9 +364,8 @@ func (c *Client) Submit(blob string) string {
common.RequestBase
TxBlob string `json:"tx_blob"`
}
- c.cmdIDs++
+
req := &Request{}
- req.ID = c.cmdIDs
req.Command = "submit"
req.TxBlob = blob
@@ -325,7 +385,7 @@ func (c *Client) SubscribeTx(hash string, callback export.Callback) {
req := Request{}
req.Command = "subscribe"
req.TxHash = hash
- c.asyncRequest(req)
+ c.syncRequest(&req)
}
//UnSubscribeTx subscribe a transaction by hash
@@ -339,7 +399,45 @@ func (c *Client) UnSubscribeTx(hash string) {
req := Request{}
req.Command = "unsubscribe"
req.TxHash = hash
- c.asyncRequest(req)
+ c.syncRequest(&req)
+}
+
+//SubscribeCtrAddr subscribe a contract by address
+func (c *Client) SubscribeCtrAddr(address string, ok bool) {
+ c.Event.SubscribeCtrAddr(address, ok)
+
+ type SubCtrAddrReq struct {
+ common.RequestBase
+ ContractAddr [1]string `json:"accounts_contract"`
+ }
+
+ ctrAddrArray := [1]string{address}
+ req := SubCtrAddrReq{}
+ req.Command = "subscribe"
+ req.ContractAddr = ctrAddrArray
+ c.syncRequest(&req)
+}
+
+//UnSubscribeCtrAddr unsubscribe a contract by address
+func (c *Client) UnSubscribeCtrAddr(address string) {
+ c.Event.UnSubscribeCtrAddr(address)
+
+ type SubCtrAddrReq struct {
+ common.RequestBase
+ ContractAddr string `json:"accounts_contract"`
+ }
+ req := SubCtrAddrReq{}
+ req.Command = "unsubscribe"
+ req.ContractAddr = address
+ c.syncRequest(&req)
+}
+
+func (c *Client) RegisterCtrEvent(eventSign string, contractMsgCh chan *data.Log) {
+ c.Event.RegisterCtrEvent(eventSign, contractMsgCh)
+}
+
+func (c *Client) UnRegisterCtrEvent(eventSign string, contractMsgCh chan *data.Log) {
+ c.Event.UnRegisterCtrEvent(eventSign, contractMsgCh)
}
func (c *Client) GetTableData(dataJSON interface{}, bSql bool) (string, error) {
@@ -350,9 +448,8 @@ func (c *Client) GetTableData(dataJSON interface{}, bSql bool) (string, error) {
SigningData string `json:"signingData"`
TxJSON interface{} `json:"tx_json"`
}
- c.cmdIDs++
+
req := &Request{}
- req.ID = c.cmdIDs
req.Command = "r_get"
if bSql {
req.Command = "r_get_sql_user"
@@ -398,7 +495,20 @@ func (c *Client) GetTableData(dataJSON interface{}, bSql bool) (string, error) {
return string(result), nil
}
+func (c *Client) SyncRequest(v common.IRequest) *Request {
+ return c.syncRequest(v)
+}
+
func (c *Client) syncRequest(v common.IRequest) *Request {
+ c.mutex.Lock()
+ c.cmdIDs++
+ c.mutex.Unlock()
+ if v.GetID() == 0 {
+ v.SetID(c.cmdIDs)
+ }
+ if c.SchemaID != "" {
+ v.SetSchemaID(c.SchemaID)
+ }
data, _ := json.Marshal(v)
request := NewRequest(v.GetID(), string(data))
request.Wait.Add(1)
@@ -434,7 +544,189 @@ func (c *Client) sendRequest(request *Request) {
c.sendMsgChan <- request.JSON
}
-func (c *Client) asyncRequest(v interface{}) {
+func (c *Client) asyncRequest(v common.IRequest) {
+ if c.SchemaID != "" {
+ v.SetSchemaID(c.SchemaID)
+ }
data, _ := json.Marshal(v)
c.sendMsgChan <- string(data)
}
+
+// GetServerInfo request for ServerInfo
+func (c *Client) GetServerInfo() (string, error) {
+ type getServerInfo struct {
+ common.RequestBase
+ }
+
+ accountReq := &getServerInfo{}
+ accountReq.Command = "server_info"
+
+ request := c.syncRequest(accountReq)
+
+ err := c.parseResponseError(request)
+ if err != nil {
+ return "", err
+ }
+
+ return request.Response.Value, nil
+}
+
+func (c *Client) GetSchemaList(params string) (string, error) {
+ account, _ := jsonparser.GetString([]byte(params), "account")
+ running, _ := jsonparser.GetBoolean([]byte(params), "running")
+ type getRunSchemaList struct {
+ common.RequestBase
+ Account string `json:"account,omitempty"`
+ Running bool `json:"running"`
+ }
+ type getSchemaList struct {
+ common.RequestBase
+ Account string `json:"account,omitempty"`
+ }
+
+ var request *Request
+ if strings.Contains(params, "running") {
+ schemaListReq := &getRunSchemaList{}
+ schemaListReq.Command = "schema_list"
+ if account != "" {
+ schemaListReq.Account = account
+ }
+ schemaListReq.Running = running
+ request = c.syncRequest(schemaListReq)
+
+ } else {
+ schemaListReq := &getSchemaList{}
+ schemaListReq.Command = "schema_list"
+ if account != "" {
+ schemaListReq.Account = account
+ }
+
+ request = c.syncRequest(schemaListReq)
+ }
+
+ err := c.parseResponseError(request)
+ if err != nil {
+ return "", err
+ }
+ return request.Response.Value, nil
+}
+func (c *Client) GetSchemaInfo(schemaID string) (string, error) {
+ if schemaID == "" {
+ return "", fmt.Errorf("Invalid parameter")
+ }
+ type getSchemaInfo struct {
+ common.RequestBase
+ Schema string `json:"schema"`
+ }
+
+ schemaInfoReq := &getSchemaInfo{}
+ schemaInfoReq.Command = "schema_info"
+ schemaInfoReq.Schema = schemaID
+ request := c.syncRequest(schemaInfoReq)
+
+ err := c.parseResponseError(request)
+ if err != nil {
+ return "", err
+ }
+ return request.Response.Value, nil
+}
+
+func (c *Client) StopSchema(schemaID string) (string, error) {
+ if schemaID == "" {
+ return "", fmt.Errorf("Invalid parameter")
+ }
+ type StopSchema struct {
+ common.RequestBase
+ Schema string `json:"schema"`
+ }
+
+ StopSchemaReq := &StopSchema{}
+ StopSchemaReq.Command = "stop"
+ StopSchemaReq.Schema = schemaID
+ request := c.syncRequest(StopSchemaReq)
+
+ err := c.parseResponseError(request)
+ if err != nil {
+ return "", err
+ }
+ return request.Response.Value, nil
+}
+
+func (c *Client) StartSchema(schemaID string) (string, error) {
+ if schemaID == "" {
+ return "", fmt.Errorf("Invalid parameter")
+ }
+ type StartSchema struct {
+ common.RequestBase
+ Schema string `json:"schema"`
+ }
+
+ StartSchemaReq := &StartSchema{}
+ StartSchemaReq.Command = "schema_start"
+ StartSchemaReq.Schema = schemaID
+ request := c.syncRequest(StartSchemaReq)
+
+ err := c.parseResponseError(request)
+ if err != nil {
+ return "", err
+ }
+ return request.Response.Value, nil
+}
+
+func (c *Client) Unsubscribe() (string, error) {
+ type Unsubscribe struct {
+ common.RequestBase
+ Streams []string `json:"streams"`
+ }
+
+ unsubCmd := &Unsubscribe{
+ RequestBase: common.RequestBase{
+ Command: "unsubscribe",
+ // ID: c.cmdIDs,
+ },
+ Streams: []string{"ledger", "server"},
+ }
+ request := c.syncRequest(unsubCmd)
+
+ err := c.parseResponseError(request)
+ if err != nil {
+ return "", err
+ }
+ return request.Response.Value, nil
+}
+
+func (c *Client) GetTransaction(hash string) (string, error) {
+ type getTransaction struct {
+ common.RequestBase
+ Transaction string `json:"transaction"`
+ }
+
+ getTransactionReq := &getTransaction{}
+ getTransactionReq.Command = "tx"
+ getTransactionReq.Transaction = hash
+ request := c.syncRequest(getTransactionReq)
+
+ err := c.parseResponseError(request)
+ if err != nil {
+ return "", err
+ }
+ return request.Response.Value, nil
+}
+
+func (c *Client) GetTransactionResult(hash string) (string, error) {
+ type getTransactionResult struct {
+ common.RequestBase
+ Transaction string `json:"transaction"`
+ }
+
+ getTxResultReq := &getTransactionResult{}
+ getTxResultReq.Command = "tx_result"
+ getTxResultReq.Transaction = hash
+ request := c.syncRequest(getTxResultReq)
+
+ err := c.parseResponseError(request)
+ if err != nil {
+ return "", err
+ }
+ return request.Response.Value, nil
+}
diff --git a/net/serverinfo.go b/net/serverinfo.go
index 7a6242e..0208f99 100644
--- a/net/serverinfo.go
+++ b/net/serverinfo.go
@@ -48,7 +48,7 @@ func (s *ServerInfo) Update(result string) {
s.GetFieldInt(result, &s.TxnCount, "txn_count")
// s.GetFieldString(result, &s.Ledgerhash, "ledger_hash")
// s.GetFieldString(result, &s.ServerStatus, "server_status")
- log.Printf("!!!!!!!!!!ServerUpdate ledger_index=%d\n", s.LedgerIndex)
+// log.Printf("!!!!!!!!!!ServerUpdate ledger_index=%d\n", s.LedgerIndex)
s.Updated = true
}
@@ -58,7 +58,7 @@ func (s *ServerInfo) GetFieldInt(result string, field *int, fieldInJSON string)
if err == nil {
*field = int(nValue)
} else if fieldInJSON == "ledger_index" {
- log.Printf("GetFieldInt error for field %s:%s\n", fieldInJSON, result)
+ //log.Printf("GetFieldInt error for field %s:%s\n", fieldInJSON, result)
}
}
diff --git a/net/utility.go b/net/utility.go
index 511f751..a107101 100644
--- a/net/utility.go
+++ b/net/utility.go
@@ -6,6 +6,8 @@ import (
"github.com/buger/jsonparser"
)
+const LastLedgerSeqOffset = 20
+
//PrepareTable return the account sequence and table NameInDB
func PrepareTable(client *Client, name string) (uint32, string, error) {
w := new(sync.WaitGroup)
@@ -39,3 +41,41 @@ func PrepareTable(client *Client, name string) (uint32, string, error) {
w.Wait()
return seq, nameInDB, err
}
+
+func PrepareRipple(client *Client) (uint32, error) {
+
+ var seq uint32 = 0
+ err := error(nil)
+
+ info, errTmp := client.GetAccountInfo(client.Auth.Address)
+ if errTmp != nil {
+ err = errTmp
+ return 0, errTmp
+ }
+ sequence, errTmp := jsonparser.GetInt([]byte(info), "result", "account_data", "Sequence")
+ if errTmp != nil {
+ err = errTmp
+ return 0, errTmp
+ }
+ seq = uint32(sequence)
+
+ return seq, err
+}
+
+func PrepareLastLedgerSeqAndFee(client *Client) (int64, uint32, error) {
+ var fee int64 = 10
+ var lastLedgerSeq uint32 = 10
+ if client.ServerInfo.Updated {
+ lastLedgerSeq = uint32(client.ServerInfo.LedgerIndex + LastLedgerSeqOffset)
+ fee = int64(client.ServerInfo.ComputeFee())
+ } else {
+ ledgerIndex, err := client.GetLedgerCurrent()
+ if err != nil {
+ return 0, 0, err
+ }
+ lastLedgerSeq = uint32(ledgerIndex + LastLedgerSeqOffset)
+
+ fee = 50
+ }
+ return fee, lastLedgerSeq, nil
+}
diff --git a/net/websocketManager.go b/net/websocketManager.go
index 7d6d423..ddb1117 100644
--- a/net/websocketManager.go
+++ b/net/websocketManager.go
@@ -1,6 +1,10 @@
package net
import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
"log"
"sync"
"time"
@@ -18,16 +22,21 @@ type WebsocketManager struct {
sendMsgChan chan string
recvMsgChan chan string
isAlive bool
+ isClose bool
timeout int // used for reconnecting
muxRead *sync.Mutex
muxWrite *sync.Mutex
muxConnect *sync.Mutex
onReconnected Reconnected
first_connect bool
+ serverName string
+ tlsRootCertPath string
+ tlsClientKeyPath string
+ tlsClientCertPath string
}
// NewWsClientManager is a constructor
-func NewWsClientManager(url string, timeout int) *WebsocketManager {
+func NewWsClientManager(url, tlsRootCertPath, tlsClientCertPath, tlsClientKeyPath, serverName string, timeout int) *WebsocketManager {
var sendChan = make(chan string, 1024)
var recvChan = make(chan string, 1024)
var conn *websocket.Conn
@@ -37,26 +46,65 @@ func NewWsClientManager(url string, timeout int) *WebsocketManager {
sendMsgChan: sendChan,
recvMsgChan: recvChan,
isAlive: false,
+ isClose: false,
timeout: timeout,
muxRead: new(sync.Mutex),
muxWrite: new(sync.Mutex),
muxConnect: new(sync.Mutex),
onReconnected: nil,
first_connect: true,
+ serverName: serverName,
+ tlsRootCertPath: tlsRootCertPath,
+ tlsClientKeyPath: tlsClientKeyPath,
+ tlsClientCertPath: tlsClientCertPath,
}
}
// 链接服务端
func (wsc *WebsocketManager) dail() error {
var err error
- log.Printf("connecting to %s", wsc.url)
+ //log.Printf("connecting to %s", wsc.url)
websocket.DefaultDialer.HandshakeTimeout = util.DIAL_TIMEOUT * time.Second
- wsc.conn, _, err = websocket.DefaultDialer.Dial(wsc.url, nil)
+
+ // tls config
+ tlsConfig := &tls.Config{
+ MaxVersion: tls.VersionTLS12,
+ }
+
+ if wsc.tlsRootCertPath != "" {
+ var caPem []byte
+ caPem, err = ioutil.ReadFile(wsc.tlsRootCertPath)
+ if err != nil {
+ return fmt.Errorf("failed to load tls root cert(path = %s), err = %v", wsc.tlsRootCertPath, err)
+ }
+ caPool := x509.NewCertPool()
+ if !caPool.AppendCertsFromPEM(caPem) {
+ return fmt.Errorf("credentials: failed to append certificates")
+ }
+
+ tlsConfig.ServerName = wsc.serverName
+ tlsConfig.RootCAs = caPool
+
+ if wsc.tlsClientCertPath != "" {
+ clientCert, err := tls.LoadX509KeyPair(wsc.tlsClientCertPath, wsc.tlsClientKeyPath)
+ tlsConfig.Certificates = []tls.Certificate{clientCert}
+ if err != nil {
+ return fmt.Errorf("failed to load tls client (cert path = %s; key path = %s), err = %v", wsc.tlsClientCertPath, wsc.tlsClientKeyPath, err)
+ }
+ }
+
+ dailer := &websocket.Dialer{TLSClientConfig: tlsConfig}
+ wsc.conn, _, err = dailer.Dial(wsc.url, nil)
+ } else {
+ wsc.conn, _, err = websocket.DefaultDialer.Dial(wsc.url, nil)
+ }
+
if err != nil {
- log.Printf("connecting to %s failed,err:%s", wsc.url, err.Error())
+ //log.Printf("connecting to %s failed,err:%s", wsc.url, err.Error())
return err
}
wsc.isAlive = true
+ wsc.isClose = false
log.Printf("connecting to %s success!", wsc.url)
return nil
}
@@ -71,8 +119,11 @@ func (wsc *WebsocketManager) Disconnect() error {
return err
}
wsc.conn = nil
+ close(wsc.sendMsgChan)
+ close(wsc.recvMsgChan)
}
+ wsc.isClose = true
wsc.isAlive = false
wsc.muxConnect.Unlock()
@@ -88,11 +139,16 @@ func (wsc *WebsocketManager) SetUrl(url string) {
func (wsc *WebsocketManager) sendMsgThread() {
go func() {
for {
+ if wsc.isClose{
+ break
+ }
if wsc.isAlive {
msg := <-wsc.sendMsgChan
-
if wsc.conn != nil {
wsc.muxWrite.Lock()
+ if wsc.conn == nil || !wsc.isAlive{
+ break
+ }
// wsc.conn.SetWriteDeadline(time.Now().Add(time.Duration(wsc.timeout)))
err := wsc.conn.WriteMessage(websocket.TextMessage, []byte(msg))
wsc.muxWrite.Unlock()
@@ -119,11 +175,20 @@ func (wsc *WebsocketManager) OnReconnected(cb Reconnected) {
func (wsc *WebsocketManager) readMsgThread() {
go func() {
for {
+ if wsc.isClose{
+ break
+ }
if wsc.conn != nil && wsc.isAlive {
wsc.muxRead.Lock()
+ if wsc.conn == nil || !wsc.isAlive{
+ break
+ }
_, message, err := wsc.conn.ReadMessage()
wsc.muxRead.Unlock()
if err != nil {
+ if wsc.conn == nil || !wsc.isAlive{
+ break
+ }
wsc.close()
log.Println("read:", err)
wsc.isAlive = false
@@ -150,8 +215,11 @@ func (wsc *WebsocketManager) close() {
func (wsc *WebsocketManager) checkReconnect() {
go func() {
for {
+ if wsc.isClose{
+ break
+ }
if !wsc.isAlive {
- log.Println("checkReconnect ws disconnected,reconnect!")
+ //log.Println("checkReconnect ws disconnected,reconnect!")
wsc.muxConnect.Lock()
err := wsc.connectAndRun()
wsc.muxConnect.Unlock()
@@ -198,7 +266,7 @@ func (wsc *WebsocketManager) ReadChan() chan string {
}
func (wsc *WebsocketManager) IsConnected() bool {
- return wsc.isAlive
+ return wsc.isAlive && !wsc.isClose
}
// func main() {
diff --git a/test/READ.md b/test/READ.md
new file mode 100644
index 0000000..9c4d946
--- /dev/null
+++ b/test/READ.md
@@ -0,0 +1,8 @@
+## 编译镜像下载:
+ docker pull peersafes/ubuntu-server-base:golang-1.17.8-gm
+## 编译:
+ ./build.sh
+## 运行:
+ ./test
+ 注:运行环境必须是linux环境
+
diff --git a/test/build.sh b/test/build.sh
new file mode 100644
index 0000000..53d372b
--- /dev/null
+++ b/test/build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+#set -x
+
+docker-compose -f ./build/build.yaml run --no-deps --rm go-sdk-build
diff --git a/test/build/build.yaml b/test/build/build.yaml
new file mode 100644
index 0000000..e6f93a6
--- /dev/null
+++ b/test/build/build.yaml
@@ -0,0 +1,13 @@
+version: '3'
+
+services:
+ go-sdk-build:
+ container_name: go-sdk-build
+ image: peersafes/ubuntu-server-base:golang-1.17.8-gm
+ environment:
+ - GODEBUG=netdns=go
+ - GO111MODULE=off
+ working_dir: /opt/gopath/src/github.com/ChainSQL/go-chainsql-api/test/
+ volumes:
+ - $GOPATH/src/github.com/ChainSQL/go-chainsql-api:/opt/gopath/src/github.com/ChainSQL/go-chainsql-api
+ command: go build .
diff --git a/test/certs/client/client.crt b/test/certs/client/client.crt
new file mode 100644
index 0000000..64175d1
--- /dev/null
+++ b/test/certs/client/client.crt
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICPDCCAeKgAwIBAgIRAL3DlaPFDf1ZUmld0fu6ONAwCgYIKoEcz1UBg3UwdjEL
+MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
+cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs
+c2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjIwNTEzMDMyNTAwWhcNMzIwNTEwMDMy
+NTAwWjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWQWRtaW5Ab3JnMS5leGFtcGxlLmNv
+bTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABHalTgV4QUVHCCBLrvTZ63aNUkzc
+hr+8hBHn45WDhNsWzwVexwIrb9hViu4lPpzRRKA5v6pVQYvtfyHdzy30pyqjbDBq
+MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
+DAYDVR0TAQH/BAIwADArBgNVHSMEJDAigCC1ICJozJDscucgrrKok+wD8UlrbAID
+koYA655LGIwZSjAKBggqgRzPVQGDdQNIADBFAiBMQSjtsz1Xnn5VzMqQ+xa1TVhE
+/ScqLs4zwyPcELzolQIhAK27dPZIeTsoY4gAynawWdnquQppagwatBXvYFJ7sel8
+-----END CERTIFICATE-----
diff --git a/test/certs/client/client.key b/test/certs/client/client.key
new file mode 100644
index 0000000..df737d7
--- /dev/null
+++ b/test/certs/client/client.key
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgUWoKJ6ldGIICHuqV
+hpEck8d55ftxey3ZLkghHIz2k2GgCgYIKoEcz1UBgi2hRANCAAR2pU4FeEFFRwgg
+S6702et2jVJM3Ia/vIQR5+OVg4TbFs8FXscCK2/YVYruJT6c0USgOb+qVUGL7X8h
+3c8t9Kcq
+-----END PRIVATE KEY-----
diff --git a/test/certs/node/server.crt b/test/certs/node/server.crt
new file mode 100644
index 0000000..3d614dd
--- /dev/null
+++ b/test/certs/node/server.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICZzCCAg6gAwIBAgIRAIhQf7H/cE4wavCbgGrgFqwwCgYIKoEcz1UBg3UwdjEL
+MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
+cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs
+c2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjIwNTEzMDMyNTAwWhcNMzIwNTEwMDMy
+NTAwWjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzEfMB0GA1UEAxMWcGVlcjAub3JnMS5leGFtcGxlLmNv
+bTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABPZyT2sNJcPUBEYM91dQCNwqD9HJ
+ZVsuYpPHUVzw0cAbL9wWEgHW55f/dnmH68Q8IY6fo89Z5O9Xy2s/YKrdgTOjgZcw
+gZQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
+AjAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAILUgImjMkOxy5yCusqiT7APxSWts
+AgOShgDrnksYjBlKMCgGA1UdEQQhMB+CFnBlZXIwLm9yZzEuZXhhbXBsZS5jb22C
+BXBlZXIwMAoGCCqBHM9VAYN1A0cAMEQCIBiAllL72WZqRt5OE+jxf/JwepisPeoV
+/OTry40qNrbFAiBWMe1QaGItDceL1l8z6tX43P+C1JUaXodnkt4P8zER+Q==
+-----END CERTIFICATE-----
diff --git a/test/certs/node/server.key b/test/certs/node/server.key
new file mode 100644
index 0000000..055e887
--- /dev/null
+++ b/test/certs/node/server.key
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgKdDEY7bQbGeDffDw
+TmNmJWu8qkMK5nexQHPBbc2C2vWgCgYIKoEcz1UBgi2hRANCAAT2ck9rDSXD1ARG
+DPdXUAjcKg/RyWVbLmKTx1Fc8NHAGy/cFhIB1ueX/3Z5h+vEPCGOn6PPWeTvV8tr
+P2Cq3YEz
+-----END PRIVATE KEY-----
diff --git a/test/certs/root/ca.crt b/test/certs/root/ca.crt
new file mode 100644
index 0000000..0da45c0
--- /dev/null
+++ b/test/certs/root/ca.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICVzCCAf2gAwIBAgIQaoOw/b1RJMntvqZzcn7k2DAKBggqgRzPVQGDdTB2MQsw
+CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
+YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz
+Y2Eub3JnMS5leGFtcGxlLmNvbTAeFw0yMjA1MTMwMzI1MDBaFw0zMjA1MTAwMzI1
+MDBaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
+Ew1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tMR8wHQYD
+VQQDExZ0bHNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoEcz1UB
+gi0DQgAEilxdQnIWsxFUmuQSB7GItyWO8oM0izVAFRviG3m0hYOFUlExlTvrNCnW
+eYBau1fUGLYI+xS0noGnOzkS5dyCCqNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1Ud
+JQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1Ud
+DgQiBCC1ICJozJDscucgrrKok+wD8UlrbAIDkoYA655LGIwZSjAKBggqgRzPVQGD
+dQNIADBFAiANIAqpAKHewXkISdv5VnxUHoAs9/sVCnijDPvu17f/2QIhAKuRgnz1
+TGVAeMiFFfnKw2YEMNTaQNFRtNl73nSbsHA8
+-----END CERTIFICATE-----
diff --git a/test/certs/server/server.crt b/test/certs/server/server.crt
new file mode 100644
index 0000000..3d614dd
--- /dev/null
+++ b/test/certs/server/server.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICZzCCAg6gAwIBAgIRAIhQf7H/cE4wavCbgGrgFqwwCgYIKoEcz1UBg3UwdjEL
+MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
+cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs
+c2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjIwNTEzMDMyNTAwWhcNMzIwNTEwMDMy
+NTAwWjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzEfMB0GA1UEAxMWcGVlcjAub3JnMS5leGFtcGxlLmNv
+bTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABPZyT2sNJcPUBEYM91dQCNwqD9HJ
+ZVsuYpPHUVzw0cAbL9wWEgHW55f/dnmH68Q8IY6fo89Z5O9Xy2s/YKrdgTOjgZcw
+gZQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
+AjAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAILUgImjMkOxy5yCusqiT7APxSWts
+AgOShgDrnksYjBlKMCgGA1UdEQQhMB+CFnBlZXIwLm9yZzEuZXhhbXBsZS5jb22C
+BXBlZXIwMAoGCCqBHM9VAYN1A0cAMEQCIBiAllL72WZqRt5OE+jxf/JwepisPeoV
+/OTry40qNrbFAiBWMe1QaGItDceL1l8z6tX43P+C1JUaXodnkt4P8zER+Q==
+-----END CERTIFICATE-----
diff --git a/test/certs/server/server.key b/test/certs/server/server.key
new file mode 100644
index 0000000..055e887
--- /dev/null
+++ b/test/certs/server/server.key
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgKdDEY7bQbGeDffDw
+TmNmJWu8qkMK5nexQHPBbc2C2vWgCgYIKoEcz1UBgi2hRANCAAT2ck9rDSXD1ARG
+DPdXUAjcKg/RyWVbLmKTx1Fc8NHAGy/cFhIB1ueX/3Z5h+vEPCGOn6PPWeTvV8tr
+P2Cq3YEz
+-----END PRIVATE KEY-----
diff --git a/test/chainsql-gm/certs/client/client.crt b/test/chainsql-gm/certs/client/client.crt
new file mode 100644
index 0000000..64175d1
--- /dev/null
+++ b/test/chainsql-gm/certs/client/client.crt
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICPDCCAeKgAwIBAgIRAL3DlaPFDf1ZUmld0fu6ONAwCgYIKoEcz1UBg3UwdjEL
+MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
+cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs
+c2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjIwNTEzMDMyNTAwWhcNMzIwNTEwMDMy
+NTAwWjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzEfMB0GA1UEAwwWQWRtaW5Ab3JnMS5leGFtcGxlLmNv
+bTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABHalTgV4QUVHCCBLrvTZ63aNUkzc
+hr+8hBHn45WDhNsWzwVexwIrb9hViu4lPpzRRKA5v6pVQYvtfyHdzy30pyqjbDBq
+MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
+DAYDVR0TAQH/BAIwADArBgNVHSMEJDAigCC1ICJozJDscucgrrKok+wD8UlrbAID
+koYA655LGIwZSjAKBggqgRzPVQGDdQNIADBFAiBMQSjtsz1Xnn5VzMqQ+xa1TVhE
+/ScqLs4zwyPcELzolQIhAK27dPZIeTsoY4gAynawWdnquQppagwatBXvYFJ7sel8
+-----END CERTIFICATE-----
diff --git a/test/chainsql-gm/certs/client/client.key b/test/chainsql-gm/certs/client/client.key
new file mode 100644
index 0000000..df737d7
--- /dev/null
+++ b/test/chainsql-gm/certs/client/client.key
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgUWoKJ6ldGIICHuqV
+hpEck8d55ftxey3ZLkghHIz2k2GgCgYIKoEcz1UBgi2hRANCAAR2pU4FeEFFRwgg
+S6702et2jVJM3Ia/vIQR5+OVg4TbFs8FXscCK2/YVYruJT6c0USgOb+qVUGL7X8h
+3c8t9Kcq
+-----END PRIVATE KEY-----
diff --git a/test/chainsql-gm/certs/node/server.crt b/test/chainsql-gm/certs/node/server.crt
new file mode 100644
index 0000000..3d614dd
--- /dev/null
+++ b/test/chainsql-gm/certs/node/server.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICZzCCAg6gAwIBAgIRAIhQf7H/cE4wavCbgGrgFqwwCgYIKoEcz1UBg3UwdjEL
+MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
+cmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRs
+c2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjIwNTEzMDMyNTAwWhcNMzIwNTEwMDMy
+NTAwWjBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzEfMB0GA1UEAxMWcGVlcjAub3JnMS5leGFtcGxlLmNv
+bTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABPZyT2sNJcPUBEYM91dQCNwqD9HJ
+ZVsuYpPHUVzw0cAbL9wWEgHW55f/dnmH68Q8IY6fo89Z5O9Xy2s/YKrdgTOjgZcw
+gZQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
+AjAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAILUgImjMkOxy5yCusqiT7APxSWts
+AgOShgDrnksYjBlKMCgGA1UdEQQhMB+CFnBlZXIwLm9yZzEuZXhhbXBsZS5jb22C
+BXBlZXIwMAoGCCqBHM9VAYN1A0cAMEQCIBiAllL72WZqRt5OE+jxf/JwepisPeoV
+/OTry40qNrbFAiBWMe1QaGItDceL1l8z6tX43P+C1JUaXodnkt4P8zER+Q==
+-----END CERTIFICATE-----
diff --git a/test/chainsql-gm/certs/node/server.key b/test/chainsql-gm/certs/node/server.key
new file mode 100644
index 0000000..055e887
--- /dev/null
+++ b/test/chainsql-gm/certs/node/server.key
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgKdDEY7bQbGeDffDw
+TmNmJWu8qkMK5nexQHPBbc2C2vWgCgYIKoEcz1UBgi2hRANCAAT2ck9rDSXD1ARG
+DPdXUAjcKg/RyWVbLmKTx1Fc8NHAGy/cFhIB1ueX/3Z5h+vEPCGOn6PPWeTvV8tr
+P2Cq3YEz
+-----END PRIVATE KEY-----
diff --git a/test/chainsql-gm/certs/root/ca.crt b/test/chainsql-gm/certs/root/ca.crt
new file mode 100644
index 0000000..0da45c0
--- /dev/null
+++ b/test/chainsql-gm/certs/root/ca.crt
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICVzCCAf2gAwIBAgIQaoOw/b1RJMntvqZzcn7k2DAKBggqgRzPVQGDdTB2MQsw
+CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
+YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz
+Y2Eub3JnMS5leGFtcGxlLmNvbTAeFw0yMjA1MTMwMzI1MDBaFw0zMjA1MTAwMzI1
+MDBaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
+Ew1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tMR8wHQYD
+VQQDExZ0bHNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoEcz1UB
+gi0DQgAEilxdQnIWsxFUmuQSB7GItyWO8oM0izVAFRviG3m0hYOFUlExlTvrNCnW
+eYBau1fUGLYI+xS0noGnOzkS5dyCCqNtMGswDgYDVR0PAQH/BAQDAgGmMB0GA1Ud
+JQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1Ud
+DgQiBCC1ICJozJDscucgrrKok+wD8UlrbAIDkoYA655LGIwZSjAKBggqgRzPVQGD
+dQNIADBFAiANIAqpAKHewXkISdv5VnxUHoAs9/sVCnijDPvu17f/2QIhAKuRgnz1
+TGVAeMiFFfnKw2YEMNTaQNFRtNl73nSbsHA8
+-----END CERTIFICATE-----
diff --git a/test/chainsql-gm/chainsqld.cfg b/test/chainsql-gm/chainsqld.cfg
new file mode 100644
index 0000000..5dfd022
--- /dev/null
+++ b/test/chainsql-gm/chainsqld.cfg
@@ -0,0 +1,146 @@
+[server]
+port_https_admin_local
+port_wss_public
+port_peer
+
+[port_https_admin_local]
+port = 5005
+ip = 0.0.0.0
+admin = 0.0.0.0
+protocol = https
+#节点https服务的密钥和证书配置
+ssl_key = ./node/server.key
+ssl_cert = ./node/server.crt
+ssl_verify = 1
+
+[port_wss_public]
+port = 6006
+ip = 0.0.0.0
+admin = 0.0.0.0
+protocol = wss
+#节点wss服务的密钥和证书配置
+ssl_key = ./node/server.key
+ssl_cert = ./node/server.crt
+ssl_verify = 1
+
+[port_peer]
+port = 5017
+ip = 0.0.0.0
+protocol = peer
+
+##节点准入的根证书配置
+#[peer_x509_root_path]
+#/home/wangchao/workplace/chainsqld_peers/peer1/ca1.crt
+#/home/wangchao/workplace/chainsqld_peers/peer1/ca2.crt
+#
+##节点准入的节点子证书配置
+#[peer_x509_cred_path]
+#/home/wangchao/workplace/chainsqld_peers/peer1/peer1.crt
+
+#-------------------------------------------------------------------------------
+
+#账户证书体系的根证书配置
+#[x509_crt_path]
+#./root.crt
+
+#-------------------------------------------------------------------------------
+
+[node_size]
+tiny
+#medium
+
+[node_db]
+type=RocksDB
+path=./data/RocksDB
+open_files=2000
+filter_bits=12
+cache_mb=256
+file_size_mb=8
+file_size_mult=2
+
+[ledger_history]
+full
+
+[database_path]
+./data
+
+[debug_logfile]
+./data/debug.log
+
+[sntp_servers]
+time.windows.com
+time.apple.com
+time.nist.gov
+pool.ntp.org
+
+# Turn down default logging to save disk space in the long run.
+# Valid values here are trace, debug, info, warning, error, and fatal
+[rpc_startup]
+{ "command" : "log_level", "severity" : "Warning" }
+{ "command" : "log_level", "partition" : "Server", "severity" : "trace" }
+
+[ssl_verify]
+0
+
+[ips]
+
+[validation_seed]
+pcL4xp1EkaDtvafHTjDEVmPUu9txadBCGMstvFzUxDmTJvFXgRX
+
+[validation_public_key]
+pEnTgGrYumV8vicA6YuPnyYXPhtXJY3W7gUHdRF9LLDtwvNVty5pZUbNTJpjXo6V7G7K8Z6uozrvUSUyvQB2xZQ9ZrbRocog
+
+[validators]
+pEnTgGrYumV8vicA6YuPnyYXPhtXJY3W7gUHdRF9LLDtwvNVty5pZUbNTJpjXo6V7G7K8Z6uozrvUSUyvQB2xZQ9ZrbRocog
+
+#[validator_list_sites]
+#http://192.168.29.69:8000
+#
+#[validator_list_keys]
+#029d1f40fc569fff2a76417008d98936a04417db0758c8ab123dee6dbd08d79398
+
+[features]
+#FeeEscalation
+MultiSign
+Escrow
+
+
+[consensus]
+type=pop
+init_time=10
+
+[veto_amendments]
+C6970A8B603D8778783B61C0D445C23D1633CCFAEF0D43E7DBCD1521D34BD7C3 SHAMapV2
+C1B8D934087225F509BEB5A8EC24447854713EE447D277F69545ABFA0E0FD490 Tickets
+1562511F573A19AE9BD103B5D6B9E01B3B46805AEC5D3C4805C902B514399146 CryptoConditions
+86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90 CryptoConditionsSuite
+#3012E8230864E95A58C60FD61430D7E1B4D3353195F2981DC12B0C7C0950FFAC FlowCross
+#42EEA5E28A97824821D4EF97081FE36A54E9593C6E4F20CBAE098C69D2E072DC fix1373
+#740352F2412A9909880C23A559FCECEDA3BE2126FED62FC7660D628A06927F11 Flow
+#E2E6F2866106419B88C50045ACE96368558C345566AC8F2BDF5A5B5587F0E6FA fix1368
+
+[governance]
+#admin=zwotLHCa2EAw8As42jRLR8jBMyQnXe67Q9
+#admin=zNPZvrBoRzkhoAvesV7pZu2rAG17ezPz4Y
+default_authority_enabled=0
+
+#[auto_sync]
+#1
+#
+#[sync_db]
+#type=mysql
+#host=10.100.0.78
+#port=3306
+#user=root
+#pass=1234
+#db=wc
+#first_storage=0
+#charset=utf8
+
+[workers]
+8
+
+[crypto_alg]
+node_alg_type=gmalg
+
+
diff --git a/test/chainsql-gm/docker-compose.yaml b/test/chainsql-gm/docker-compose.yaml
new file mode 100644
index 0000000..632749a
--- /dev/null
+++ b/test/chainsql-gm/docker-compose.yaml
@@ -0,0 +1,28 @@
+version: '2'
+services:
+ chainsql:
+ container_name: chainsql
+ image: peersafes/chainsql:latest
+ restart: always
+ ports:
+ - 5005:5005
+ - 6006:6006
+ - 5017:5017
+ ulimits:
+ nproc: 65535
+ nofile:
+ soft: 65535
+ hard: 65535
+ volumes:
+ - ./certs/node:/opt/chainsql/node
+ - ./certs/root:/opt/chainsql/root
+ - /etc/localtime:/etc/localtime
+ - ./update:/usr/local/bin/update
+ - ./chainsqld.cfg:/opt/chainsql/chainsqld.cfg
+
+ entrypoint: ["/bin/sh","-c","./chainsqld"]
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "20m"
+ max-file: "100"
diff --git a/test/chainsql-gm/update b/test/chainsql-gm/update
new file mode 100644
index 0000000..aa31856
Binary files /dev/null and b/test/chainsql-gm/update differ
diff --git a/test/main.go b/test/main.go
index f0dd6e7..e06bd22 100644
--- a/test/main.go
+++ b/test/main.go
@@ -4,9 +4,11 @@ import (
"encoding/json"
"fmt"
"log"
+ "math/big"
"time"
"github.com/ChainSQL/go-chainsql-api/core"
+ "github.com/ChainSQL/go-chainsql-api/test/storage"
"github.com/buger/jsonparser"
"github.com/gorilla/websocket"
)
@@ -17,45 +19,103 @@ type Account struct {
secret string
}
+var root1 = Account{
+ address: "zPHxWCKNZjpbQHV5DLpBy8rSR8HdXpPDzi",
+ secret: "xp6FwxZP1rrmPy2GDTobvHTgnZnrC",
+}
+
+//zPHxWCKNZjpbQHV5DLpBy8rSR8HdXpPDzi
+//zEdmwPS5BoEukn1ech2LHjuV6STkq3QYkM
+var root = Account{
+ address: "zHb9CJAWyB4zj91VRWn96DkukG4bwdtyTh",
+ secret: "xnoPBzXtMeMyMHUVTgbuqAfg1SUTb",
+}
+var user1 = Account{
+ address: "zBonp9s7isAaDUPcfrFfYjNnhgeznoBHxF",
+ secret: "xn2FhQLRQqhKJeNhpgMzp2PGAYbdw",
+}
+var user2 = Account{
+ address: "zKXfeKXkTtLSTkEzaJyu2cRmRBFRvTW2zc",
+ secret: "xhtBo8BLBZtTgc3LHnRspaFro5P4H",
+}
+var gmRoot = Account{
+ address: "zN7TwUjJ899xcvNXZkNJ8eFFv2VLKdESsj",
+ secret: "p97evg5Rht7ZB7DbEpVqmV3yiSBMxR3pRBKJyLcRWt7SL5gEeBb",
+}
+var smUser1 = Account{
+ secret: "pwRdHmA4cSUKKtFyo4m2vhiiz5g6ym58Noo9dTsUU97mARNjevj",
+ address: "zMXMtS2C36J1p3uhTxRFWV8pEhHa8AMMSL",
+}
var tableName = "hello2"
func main() {
c := core.NewChainsql()
- // err := c.Connect("ws://127.0.0.1:6006")
- // log.Println("IsConnected:", c.IsConnected())
- // if err != nil {
- // log.Println(err)
- // return
- // }
+
+ // address := "wss://127.0.0.1:6006"
+ address := "ws://127.0.0.1:6006"
+
+ serverName := "peer0.org1.example.com"
+ rootPath := "./certs/root/ca.crt"
+ clientCertPath := "./certs/client/client.crt"
+ clientKeyPath := "./certs/client/client.key"
+
+ serverName = ""
+ rootPath = ""
+ clientCertPath = ""
+ clientKeyPath = ""
+
+ err := c.Connect(address, rootPath, clientCertPath, clientKeyPath, serverName)
+ log.Println("IsConnected:", c.IsConnected())
+ if err != nil {
+ log.Println(err)
+ return
+ }
// var root = Account{
// address: "zHb9CJAWyB4zj91VRWn96DkukG4bwdtyTh",
// secret: "xnoPBzXtMeMyMHUVTgbuqAfg1SUTb",
// }
- // var user = Account{
- // address: "zBonp9s7isAaDUPcfrFfYjNnhgeznoBHxF",
- // secret: "xn2FhQLRQqhKJeNhpgMzp2PGAYbdw",
- // }
- // c.As(user.address, user.secret)
- // c.As(root.address, root.secret)
+ c.As(gmRoot.address, gmRoot.secret)
+ //c.As(smRoot.address, smRoot.secret)
+ //c.SetSchema("44C2C733C17335C11B01BCB0B55340EA422F37307188FF84E6127F8BEBBF0C60")
+ //GenerateKey(rand.Reader)
// c.Use(root.address)
- // // testSubLedger(c)
- testGenerateAccount(c)
- // testInsert(c)
+ testContract(c)
+ // testSubLedger(c)
+ // testGenerateAccount(c)
+ //testInsert(c)
// testGetLedger(c)
+ // testGetLedgerTxs(c)
+ // testGetLedgerVersion(c)
// testSignPlainText(c)
// testGetTableData(c)
// testGetBySqlUser(c)
// testWebsocket()
- // testTickerGet(c)
+ // testTickerGet(c)\
+ //testValidationCreate(c)
+ // testGetAccountInfo(c)
+ // testGetServerInfo(c)
+ // testPay(c)
+ //testSchemaCreate(c) //创建子链
+ //testSchemaModify(c) // 修改子链
+ //testGetSchemaList(c) //获取子链列表
+ //testGetSchemaInfo(c) //依据子链id获取子链信息
+ //testStopSchema(c) //
+ //testStartSchema(c)
+
+ // testGetTransaction(c)
+ //testGetSchemaId(c)
+ // testGenerateAddress(c)
+ //testDeleteSchema(c)
+ // testGetTransactionResult(c)
for {
- time.Sleep(time.Second * 10)
+ time.Sleep(time.Second * 1)
}
}
-func testGenerateAccount(c *core.Chainsql) {
+/*func testGenerateAccount(c *core.Chainsql) {
accStr, err := c.GenerateAccount()
if err != nil {
log.Println(err)
@@ -71,11 +131,11 @@ func testGenerateAccount(c *core.Chainsql) {
return
}
log.Println(accStr)
-}
+}*/
func testInsert(c *core.Chainsql) {
var data = []byte(`[{"id":1,"name":"echo","age":18}]`)
- ret := c.Table(tableName).Insert(string(data)).Submit("db_success")
+ ret := c.Table("gmTest50").Insert(string(data)).Submit("db_success")
log.Println(ret)
}
@@ -140,6 +200,21 @@ func testGetLedger(c *core.Chainsql) {
}
}
+func testGetLedgerTxs(c *core.Chainsql) {
+ for i := 20; i < 25; i++ {
+ ledger := c.GetLedgerTransactions(i, true)
+ log.Printf("GetLedgerTransactions %d:%s\n", i, ledger)
+ }
+}
+
+func testGetLedgerVersion(c *core.Chainsql) {
+ ledger, err := c.GetLedgerVersion()
+ if err != nil {
+ log.Println("error:", err)
+ }
+ log.Println("GetLedgerVersion", ledger)
+}
+
func testSubLedger(c *core.Chainsql) {
go func() {
c.OnLedgerClosed(func(msg string) {
@@ -206,3 +281,193 @@ func testTickerGet(c *core.Chainsql) {
ticker.Stop()
done <- true
}
+
+func testValidationCreate(c *core.Chainsql) {
+ seedKey, err := c.ValidationCreate()
+ if err != nil {
+ log.Println(err)
+ }
+ log.Printf("seedKey %s\n", seedKey)
+}
+
+func testGetAccountInfo(c *core.Chainsql) {
+ account, err := c.GetAccountInfo(gmRoot.address)
+ if err != nil {
+ log.Println(err)
+ }
+ log.Printf("seedKey %s\n", account)
+}
+
+func testGetServerInfo(c *core.Chainsql) {
+ serverInfo, err := c.GetServerInfo()
+ if err != nil {
+ log.Println(err)
+ }
+ log.Printf("seedKey %s\n", serverInfo)
+}
+
+func testContract(c *core.Chainsql) {
+ testDeployContract(c)
+
+ // contractAddr := "zwbCuvYRupsr2MRUMyTrz1eft28RewQuQJ"
+ // testInvokeContract(c, contractAddr)
+}
+func testDeployContract(c *core.Chainsql) {
+ transactOpt := &core.TransactOpts{
+ ContractValue: 0,
+ Gas: 3000000,
+ Expectation: "validate_success",
+ }
+
+ deployRet, _, _ := storage.DeployStorage(c, transactOpt)
+ log.Println(deployRet)
+}
+func testInvokeContract(c *core.Chainsql, contractAddr string) {
+ storageIns, _ := storage.NewStorage(c, contractAddr)
+
+ // testCallContract(c, storageIns)
+ // testEventContract(c, storageIns)
+ // testSubmitContract(c, storageIns)
+ testGetPastEvent(c, storageIns)
+ log.Println("finish all")
+}
+func testCallContract(c *core.Chainsql, storageIns *storage.Storage) {
+ callOpt := &core.CallOpts{}
+ ret, _ := storageIns.Retrieve(callOpt)
+ log.Println(ret)
+}
+func testSubmitContract(c *core.Chainsql, storageIns *storage.Storage) {
+ transactOpt := &core.TransactOpts{
+ ContractValue: 0,
+ Gas: 3000000,
+ Expectation: "validate_success",
+ }
+
+ ret, err := storageIns.Store(transactOpt, big.NewInt(789))
+ if err != nil {
+ log.Fatalln(err)
+ } else {
+ log.Println(ret)
+ }
+}
+func testEventContract(c *core.Chainsql, storageIns *storage.Storage) {
+ watchOpt := &core.WatchOpts{}
+ sinkCh := make(chan *storage.StorageNumberChanges)
+ event, err := storageIns.WatchNumberChanges(watchOpt, sinkCh)
+ if err != nil {
+ event.Unsubscribe()
+ }
+ go func() {
+ for logInfo := range sinkCh {
+ log.Println(logInfo)
+ }
+ }()
+ // event.Unsubscribe()
+}
+
+func testGetPastEvent(c *core.Chainsql, storageIns *storage.Storage) {
+ // eventLogs, err := storageIns.GetPastEvent("283CC2AF0AC3913FD07D9967891DCEB3B95B39D85E09429D202588B1CA9B1C42", "")
+ eventLogs, err := storageIns.GetNumberChangesPastEvent("", "5B0A2020207B0A20202020202022636F6E74726163745F6461746122203A202230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030333135222C0A20202020202022636F6E74726163745F746F7069637322203A205B20223131363144363745334534304436344441304632324634313035343132304237343541323841413235453635443938443135334642414634443331393532353122205D0A2020207D0A5D0A")
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ for _, eventLog := range eventLogs {
+ log.Println(eventLog)
+ }
+}
+
+func testPay(c *core.Chainsql) {
+ ret := c.Pay(smUser1.address, 6000000).Submit("validate_success")
+ log.Println(ret)
+}
+
+func testSchemaCreate(c *core.Chainsql) {
+ schemaInfo := "{\"SchemaName\":\"hello\",\"WithState\":true,\"SchemaAdmin\":\"zHb9CJAWyB4zj91VRWn96DkukG4bwdtyTh\",\"Validators\":[{\"Validator\":{\"PublicKey\":\"02E273A1B6C5D8427F60F490FC8E03D52F35D1A57B8755E19DB0F3B683D9A81239\"}},{\"Validator\":{\"PublicKey\":\"03946B1514971B645F22EE9C96843C5705D388D65C563B7DDEB8B56E29A5283D3C\"}},{\"Validator\":{\"PublicKey\":\"026C3AB1DE848906C5F42316D2B92C3382EEE68FC08F738A2BE9D8C0BC4954936D\"}},{\"Validator\":{\"PublicKey\":\"030F9602B680A71D962111CC3EF9D27601EB9FEC7C3BA24BB4323D94C3A1CF9A04\"}}],\"PeerList\":[{\"Peer\":{\"Endpoint\":\"10.100.0.78:25413\"}},{\"Peer\":{\"Endpoint\":\"10.100.0.78:25414\"}},{\"Peer\":{\"Endpoint\":\"10.100.0.78:25415\"}},{\"Peer\":{\"Endpoint\":\"10.100.0.104:5510\"}}]}"
+ //schemaInfo := "{"SchemaName":"子链1","WithState":false,"SchemaAdmin":"zKTqp9kqJBag59YGmL7imH9RfukG6qVtfS","Validators":[{"Validator":{"PublicKey":"037B2D1B1C97A996B44A2FA25765DE5D937247840C960AC6E84D0E3AA8A718F96E"}},{"Validator":{"PublicKey":"038C4245389C8AB8C7665CA4002AEE75EF5D7EEB51A4410D48797BC74F275E9CC3"}},{"Validator":{"PublicKey":"0237788307F53E50D9F799F0D0ABD48258BC41D9418638BD51C481D1848E005443"}}],"PeerList":[{"Peer":{"Endpoint":"192.168.0.242:12260"}},{"Peer":{"Endpoint":"192.168.0.242:12264"}},{"Peer":{"Endpoint":"192.168.0.242:12269"}}]}"
+ //schemaInfo := "{\"SchemaName\":\"hello\",\"WithState\":true,\"SchemaAdmin\":\"zN7TwUjJ899xcvNXZkNJ8eFFv2VLKdESsj\",\"Validators\":[{\"Validator\":{\"PublicKey\":\"47F7288B41B45F49342FAC6B65EC529B5ED52F3DDD35140C53BB54A3A7D03F3E9166B0FD574F098F2F9E30526EC8293CE95D4956AD8EC02B34060F0709DCDEA3C5\"}},{\"Validator\":{\"PublicKey\":\"47594A1F76382A89A811B485E3B3414F18967C55A9A2BB90DF7EF36FFF5FDCB915B9C495D66ADEA79DAD97C897596F6FE093C7CDADF90BDD0C91B99D8D014C1B05\"}},{\"Validator\":{\"PublicKey\":\"47C45A7D125E49FDFF1DE6C08F738122FFDC7171E0F5AFA794D02198E35F7F1B1F07CE271CDBF9BA4DB94AA087BE4F59F2A15A60868BE4ACFA86D13B448CD06038\"}}],\"PeerList\":[{\"Peer\":{\"Endpoint\":\"192.168.177.109:5432\"}},{\"Peer\":{\"Endpoint\":\"192.168.177.109:5433\"}},{\"Peer\":{\"Endpoint\":\"192.168.177.109:5441\"}}]}"
+ ret := c.CreateSchema(schemaInfo).Submit("validate_success")
+ log.Println(ret)
+}
+
+func testSchemaModify(c *core.Chainsql) {
+ schemaInfo := "{\"SchemaID\":\"2A4C77CF90F1EE9495733D95549B6E0C2ECD61A49343D04C75FE2FD366366A13\",\"Validators\":[{\"Validator\":{\"PublicKey\":\"471D5247096A08552746B5E321E925639A43D6A3ED5F48E48C678E1630F6B92F88EBE20579DCEF85371C43D7305787CAA9AADF7D705BDD1523BBCCF9865FEB34A4\"}}],\"PeerList\":[{\"Peer\":{\"Endpoint\":\"10.100.0.104:5410\"}}]}"
+ //schemaInfo := "{\"SchemaName\":\"hello\",\"WithState\":false,\"SchemaAdmin\":\"zBonp9s7isAaDUPcfrFfYjNnhgeznoBHxF\",\"Validators\":\"fhfhhfhfhfhfh\",\"PeerList\":[{\"Peer\":{\"Endpoint\":\"127.0.0.1:15125\"}},{\"Peer\":{\"Endpoint\":\"127.0.0.1:25125\"}},{\"Peer\":{\"Endpoint\":\"127.0.0.1:35125\"}}]}"
+ ret := c.ModifySchema("schema_add", schemaInfo).Submit("validate_success")
+ log.Println(ret)
+}
+
+func testGetSchemaList(c *core.Chainsql) {
+ //param := "{\"running\":false}"
+ param := ""
+ ret, err := c.GetSchemaList(param)
+ log.Println(ret)
+ log.Println(err)
+}
+
+func testGetSchemaInfo(c *core.Chainsql) {
+ schemaID := "44C2C733C17335C11B01BCB0B55340EA422F37307188FF84E6127F8BEBBF0C60"
+ ret, err := c.GetSchemaInfo(schemaID)
+ log.Println(ret)
+ log.Println(err)
+}
+
+func testStopSchema(c *core.Chainsql) {
+ schemaID := "2A4C77CF90F1EE9495733D95549B6E0C2ECD61A49343D04C75FE2FD366366A13"
+ ret, err := c.StopSchema(schemaID)
+ log.Println(ret)
+ log.Println(err)
+}
+
+func testStartSchema(c *core.Chainsql) {
+ schemaID := "2A4C77CF90F1EE9495733D95549B6E0C2ECD61A49343D04C75FE2FD366366A13"
+ ret, err := c.StartSchema(schemaID)
+ log.Println(ret)
+ log.Println(err)
+}
+
+func testGetTransaction(c *core.Chainsql) {
+ txHash := "283CC2AF0AC3913FD07D9967891DCEB3B95B39D85E09429D202588B1CA9B1C42"
+ ret, err := c.GetTransaction(txHash)
+ log.Println(ret)
+ log.Println(err)
+}
+
+func testGetSchemaId(c *core.Chainsql) {
+ txHash := "1E1EA9E9936574D17646EE9801B72B106DB35D13923FE4357746AD2DD2135C78"
+ ret, err := c.GetSchemaId(txHash)
+ log.Println(ret)
+ log.Println(err)
+}
+
+func testGenerateAddress(c *core.Chainsql) {
+ //option := ""
+ option := "{\"algorithm\":\"softGMAlg\"}"
+ // option := "{\"algorithm\":\"softGMAlg\", \"secret\":\"pwRdHmA4cSUKKtFyo4m2vhiiz5g6ym58Noo9dTsUU97mARNjevj\"}"
+ //xp6FwxZP1rrmPy2GDTobvHTgnZnrC
+ //xnoPBzXtMeMyMHUVTgbuqAfg1SUTb
+ //option := "{\"algorithm\":\"secp256k1\", \"secret\":\"xp6FwxZP1rrmPy2GDTobvHTgnZnrC\"}"
+ ret, err := c.GenerateAddress(option)
+ if err != nil {
+ log.Println(err)
+ } else {
+ log.Println(ret)
+ }
+}
+
+func testDeleteSchema(c *core.Chainsql) {
+ schemaID := "362C1B00328F08A3CE093FE64B0BC88951D364DC6D2B44601268E7F273354B4A"
+ ret := c.DeleteSchema(schemaID).Submit("validate_success")
+ log.Println(ret)
+}
+
+func testGetTransactionResult(c *core.Chainsql) {
+ txHash := "48AF80C9169790BE33CBD8C5F32151C9D4483D855B195D188854636EB6551AF5"
+ ret, err := c.GetTransactionResult(txHash)
+ if err != nil {
+ log.Println(err)
+ } else {
+ // {"id":2,"result":{"ledger_index":28,"transaction_result":"tesSUCCESS","tx_hash":"48AF80C9169790BE33CBD8C5F32151C9D4483D855B195D188854636EB6551AF5","tx_status":"validated"},"status":"success","type":"response"}
+ log.Println(ret)
+ }
+}
diff --git a/test/storage/storage.abi b/test/storage/storage.abi
new file mode 100644
index 0000000..ec6f37b
--- /dev/null
+++ b/test/storage/storage.abi
@@ -0,0 +1,41 @@
+[
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "num",
+ "type": "uint256"
+ }
+ ],
+ "name": "numberChanges",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "retrieve",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "num",
+ "type": "uint256"
+ }
+ ],
+ "name": "store",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+]
\ No newline at end of file
diff --git a/test/storage/storage.bin b/test/storage/storage.bin
new file mode 100644
index 0000000..7e238a1
--- /dev/null
+++ b/test/storage/storage.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b5060bd8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d14604c575b600080fd5b60005460405190815260200160405180910390f35b605b60573660046098565b605d565b005b60008190556040518181527f1161d67e3e40d64da0f22f41054120b745a28aa25e65d98d153fbaf4d31952519060200160405180910390a150565b60006020828403121560a957600080fd5b503591905056fea164736f6c6343000805000a
\ No newline at end of file
diff --git a/test/storage/storage.go b/test/storage/storage.go
new file mode 100644
index 0000000..d928f5c
--- /dev/null
+++ b/test/storage/storage.go
@@ -0,0 +1,412 @@
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package storage
+
+import (
+ "errors"
+ "math/big"
+ "strings"
+
+ "github.com/ChainSQL/go-chainsql-api/abigen/abi"
+ "github.com/ChainSQL/go-chainsql-api/abigen/abi/bind"
+ "github.com/ChainSQL/go-chainsql-api/common"
+ "github.com/ChainSQL/go-chainsql-api/core"
+ "github.com/ChainSQL/go-chainsql-api/data"
+ "github.com/ChainSQL/go-chainsql-api/event"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var (
+ _ = errors.New
+ _ = big.NewInt
+ _ = strings.NewReader
+ _ = bind.Bind
+ _ = common.Big1
+)
+
+// StorageMetaData contains all meta data concerning the Storage contract.
+var StorageMetaData = &core.CtrMetaData{
+ ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"num\",\"type\":\"uint256\"}],\"name\":\"numberChanges\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"retrieve\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"num\",\"type\":\"uint256\"}],\"name\":\"store\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
+ Bin: "0x608060405234801561001057600080fd5b5060bd8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d14604c575b600080fd5b60005460405190815260200160405180910390f35b605b60573660046098565b605d565b005b60008190556040518181527f1161d67e3e40d64da0f22f41054120b745a28aa25e65d98d153fbaf4d31952519060200160405180910390a150565b60006020828403121560a957600080fd5b503591905056fea164736f6c6343000805000a",
+}
+
+// StorageABI is the input ABI used to generate the binding from.
+// Deprecated: Use StorageMetaData.ABI instead.
+var StorageABI = StorageMetaData.ABI
+
+// StorageBin is the compiled bytecode used for deploying new contracts.
+// Deprecated: Use StorageMetaData.Bin instead.
+var StorageBin = StorageMetaData.Bin
+
+// DeployStorage deploys a new ChainSQL contract, binding an instance of Storage to it.
+func DeployStorage(chainsql *core.Chainsql, auth *core.TransactOpts) (*core.DeployTxRet, *Storage, error) {
+ parsed, err := StorageMetaData.GetAbi()
+ if err != nil {
+ return &core.DeployTxRet{}, nil, err
+ }
+ if parsed == nil {
+ return &core.DeployTxRet{}, nil, errors.New("GetABI returned nil")
+ }
+
+ deployRet, contract, err := core.DeployContract(chainsql, auth, *parsed, common.FromHex(StorageBin))
+ if err != nil {
+ return &core.DeployTxRet{}, nil, err
+ }
+ return deployRet, &Storage{StorageCaller: StorageCaller{contract: contract}, StorageTransactor: StorageTransactor{contract: contract}, StorageFilterer: StorageFilterer{contract: contract}}, nil
+}
+
+// Storage is an auto generated Go binding around an ChainSQL contract.
+type Storage struct {
+ StorageCaller // Read-only binding to the contract
+ StorageTransactor // Write-only binding to the contract
+ StorageFilterer // Log filterer for contract events
+}
+
+// StorageCaller is an auto generated read-only Go binding around an ChainSQL contract.
+type StorageCaller struct {
+ contract *core.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// StorageTransactor is an auto generated write-only Go binding around an ChainSQL contract.
+type StorageTransactor struct {
+ contract *core.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// StorageFilterer is an auto generated log filtering Go binding around an ChainSQL contract events.
+type StorageFilterer struct {
+ contract *core.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// StorageSession is an auto generated Go binding around an ChainSQL contract,
+// with pre-set call and transact options.
+type StorageSession struct {
+ Contract *Storage // Generic contract binding to set the session for
+ CallOpts core.CallOpts // Call options to use throughout this session
+ TransactOpts core.TransactOpts // Transaction auth options to use throughout this session
+}
+
+// StorageCallerSession is an auto generated read-only Go binding around an ChainSQL contract,
+// with pre-set call options.
+type StorageCallerSession struct {
+ Contract *StorageCaller // Generic contract caller binding to set the session for
+ CallOpts core.CallOpts // Call options to use throughout this session
+}
+
+// StorageTransactorSession is an auto generated write-only Go binding around an ChainSQL contract,
+// with pre-set transact options.
+type StorageTransactorSession struct {
+ Contract *StorageTransactor // Generic contract transactor binding to set the session for
+ TransactOpts core.TransactOpts // Transaction auth options to use throughout this session
+}
+
+// StorageRaw is an auto generated low-level Go binding around an ChainSQL contract.
+type StorageRaw struct {
+ Contract *Storage // Generic contract binding to access the raw methods on
+}
+
+// StorageCallerRaw is an auto generated low-level read-only Go binding around an ChainSQL contract.
+type StorageCallerRaw struct {
+ Contract *StorageCaller // Generic read-only contract binding to access the raw methods on
+}
+
+// StorageTransactorRaw is an auto generated low-level write-only Go binding around an ChainSQL contract.
+type StorageTransactorRaw struct {
+ Contract *StorageTransactor // Generic write-only contract binding to access the raw methods on
+}
+
+// NewStorage creates a new instance of Storage, bound to a specific deployed contract.
+func NewStorage(chainsql *core.Chainsql, address string) (*Storage, error) {
+ contract, err := bindStorage(chainsql, address)
+ if err != nil {
+ return nil, err
+ }
+ return &Storage{StorageCaller: StorageCaller{contract: contract}, StorageTransactor: StorageTransactor{contract: contract}, StorageFilterer: StorageFilterer{contract: contract}}, nil
+}
+
+// // NewStorageCaller creates a new read-only instance of Storage, bound to a specific deployed contract.
+// func NewStorageCaller(address common.Address, caller bind.ContractCaller) (*StorageCaller, error) {
+// contract, err := bindStorage(address, caller, nil, nil)
+// if err != nil {
+// return nil, err
+// }
+// return &StorageCaller{contract: contract}, nil
+// }
+
+// // NewStorageTransactor creates a new write-only instance of Storage, bound to a specific deployed contract.
+// func NewStorageTransactor(address common.Address, transactor bind.ContractTransactor) (*StorageTransactor, error) {
+// contract, err := bindStorage(address, nil, transactor, nil)
+// if err != nil {
+// return nil, err
+// }
+// return &StorageTransactor{contract: contract}, nil
+// }
+
+// // NewStorageFilterer creates a new log filterer instance of Storage, bound to a specific deployed contract.
+// func NewStorageFilterer(address common.Address, filterer bind.ContractFilterer) (*StorageFilterer, error) {
+// contract, err := bindStorage(address, nil, nil, filterer)
+// if err != nil {
+// return nil, err
+// }
+// return &StorageFilterer{contract: contract}, nil
+// }
+
+// bindStorage binds a generic wrapper to an already deployed contract.
+func bindStorage(chainsql *core.Chainsql, address string) (*core.BoundContract, error) {
+ parsed, err := abi.JSON(strings.NewReader(StorageABI))
+ if err != nil {
+ return nil, err
+ }
+ return core.NewBoundContract(chainsql, address, parsed), nil
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+// func (_Storage *StorageRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+// return _Storage.Contract.StorageCaller.contract.Call(opts, result, method, params...)
+// }
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+// func (_Storage *StorageRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+// return _Storage.Contract.StorageTransactor.contract.Transfer(opts)
+// }
+
+// Transact invokes the (paid) contract method with params as input values.
+// func (_Storage *StorageRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+// return _Storage.Contract.StorageTransactor.contract.Transact(opts, method, params...)
+// }
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+// func (_Storage *StorageCallerRaw) Call(opts *core.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+// return _Storage.Contract.contract.Call(opts, result, method, params...)
+// }
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+// func (_Storage *StorageTransactorRaw) Transfer(opts *core.TransactOpts) (*types.Transaction, error) {
+// return _Storage.Contract.contract.Transfer(opts)
+// }
+
+// Transact invokes the (paid) contract method with params as input values.
+// func (_Storage *StorageTransactorRaw) Transact(opts *core.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+// return _Storage.Contract.contract.Transact(opts, method, params...)
+// }
+
+// Retrieve is a free data retrieval call binding the contract method 0x2e64cec1.
+//
+// Solidity: function retrieve() view returns(uint256)
+func (_Storage *StorageCaller) Retrieve(opts *core.CallOpts) (*big.Int, error) {
+ var out []interface{}
+ err := _Storage.contract.Call(opts, &out, "retrieve")
+
+ if err != nil {
+ return *new(*big.Int), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
+
+ return out0, err
+
+}
+
+// Retrieve is a free data retrieval call binding the contract method 0x2e64cec1.
+//
+// Solidity: function retrieve() view returns(uint256)
+func (_Storage *StorageSession) Retrieve() (*big.Int, error) {
+ return _Storage.Contract.Retrieve(&_Storage.CallOpts)
+}
+
+// Retrieve is a free data retrieval call binding the contract method 0x2e64cec1.
+//
+// Solidity: function retrieve() view returns(uint256)
+func (_Storage *StorageCallerSession) Retrieve() (*big.Int, error) {
+ return _Storage.Contract.Retrieve(&_Storage.CallOpts)
+}
+
+// Store is a paid mutator transaction binding the contract method 0x6057361d.
+//
+// Solidity: function store(uint256 num) returns()
+func (_Storage *StorageTransactor) Store(opts *core.TransactOpts, num *big.Int) (*common.TxResult, error) {
+ return _Storage.contract.Transact(opts, "store", num)
+}
+
+// Store is a paid mutator transaction binding the contract method 0x6057361d.
+//
+// Solidity: function store(uint256 num) returns()
+func (_Storage *StorageSession) Store(num *big.Int) (*common.TxResult, error) {
+ return _Storage.Contract.Store(&_Storage.TransactOpts, num)
+}
+
+// Store is a paid mutator transaction binding the contract method 0x6057361d.
+//
+// Solidity: function store(uint256 num) returns()
+func (_Storage *StorageTransactorSession) Store(num *big.Int) (*common.TxResult, error) {
+ return _Storage.Contract.Store(&_Storage.TransactOpts, num)
+}
+
+// StorageNumberChangesIterator is returned from FilterNumberChanges and is used to iterate over the raw logs and unpacked data for NumberChanges events raised by the Storage contract.
+type StorageNumberChangesIterator struct {
+ Event *StorageNumberChanges // Event containing the contract specifics and raw log
+
+ contract *core.BoundContract // Generic contract to use for unpacking event data
+ event string // Event name to use for unpacking event data
+
+ logs chan data.Log // Log channel receiving the found contract events
+ sub event.Subscription // Subscription for errors, completion and termination
+ done bool // Whether the subscription completed delivering logs
+ fail error // Occurred error to stop iteration
+}
+
+// Next advances the iterator to the subsequent event, returning whether there
+// are any more events found. In case of a retrieval or parsing error, false is
+// returned and Error() can be queried for the exact failure.
+func (it *StorageNumberChangesIterator) Next() bool {
+ // If the iterator failed, stop iterating
+ if it.fail != nil {
+ return false
+ }
+ // If the iterator completed, deliver directly whatever's available
+ if it.done {
+ select {
+ case log := <-it.logs:
+ it.Event = new(StorageNumberChanges)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ default:
+ return false
+ }
+ }
+ // Iterator still in progress, wait for either a data or an error event
+ select {
+ case log := <-it.logs:
+ it.Event = new(StorageNumberChanges)
+ if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
+ it.fail = err
+ return false
+ }
+ it.Event.Raw = log
+ return true
+
+ case err := <-it.sub.Err():
+ it.done = true
+ it.fail = err
+ return it.Next()
+ }
+}
+
+// Error returns any retrieval or parsing error occurred during filtering.
+func (it *StorageNumberChangesIterator) Error() error {
+ return it.fail
+}
+
+// Close terminates the iteration process, releasing any pending underlying
+// resources.
+func (it *StorageNumberChangesIterator) Close() error {
+ it.sub.Unsubscribe()
+ return nil
+}
+
+// StorageNumberChanges represents a NumberChanges event raised by the Storage contract.
+type StorageNumberChanges struct {
+ Num *big.Int
+ Raw data.Log // Blockchain specific contextual infos
+}
+
+// FilterNumberChanges is a free log retrieval operation binding the contract event 0x1161d67e3e40d64da0f22f41054120b745a28aa25e65d98d153fbaf4d3195251.
+//
+// Solidity: event numberChanges(uint256 num)
+// func (_Storage *StorageFilterer) FilterNumberChanges(opts *core.FilterOpts) (*StorageNumberChangesIterator, error) {
+//
+//
+
+// logs, sub, err := _Storage.contract.FilterLogs(opts, "numberChanges")
+// if err != nil {
+// return nil, err
+// }
+// return &StorageNumberChangesIterator{contract: _Storage.contract, event: "numberChanges", logs: logs, sub: sub}, nil
+// }
+
+// WatchNumberChanges is a free log subscription operation binding the contract event 0x1161d67e3e40d64da0f22f41054120b745a28aa25e65d98d153fbaf4d3195251.
+//
+// Solidity: event numberChanges(uint256 num)
+func (_Storage *StorageFilterer) WatchNumberChanges(opts *core.WatchOpts, sink chan<- *StorageNumberChanges) (event.Subscription, error) {
+
+ sub, err := _Storage.contract.WatchLogs(opts, "numberChanges")
+ if err != nil {
+ return nil, err
+ }
+ return event.NewSubscription(func(quit <-chan struct{}) error {
+ defer sub.UnSubscribe()
+ for {
+ select {
+ case log := <-sub.EventMsgCh:
+ // New log arrived, parse the event and forward to the user
+ event := new(StorageNumberChanges)
+ if err := _Storage.contract.UnpackLog(event, "numberChanges", *log); err != nil {
+ return err
+ }
+ event.Raw = *log
+
+ select {
+ case sink <- event:
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ case err := <-sub.Err():
+ return err
+ case <-quit:
+ return nil
+ }
+ }
+ }), nil
+}
+
+func (_Storage *StorageFilterer) GetNumberChangesPastEvent(txHash string, ContractLogs string) ([]*StorageNumberChanges, error) {
+ var logRaws []*data.Log
+ var err error
+ if ContractLogs != "" {
+ logRaws, err = _Storage.contract.GetPastEventByCtrLog(ContractLogs)
+ } else if txHash != "" {
+ logRaws, err = _Storage.contract.GetPastEventByTxHash(txHash)
+ } else {
+ return nil, errors.New("both txHash or ContractLogs is not provided for param")
+ }
+
+ if err != nil {
+ return nil, err
+ }
+ var events []*StorageNumberChanges
+ for _, logRaw := range logRaws {
+ event, err := _Storage.ParseNumberChanges(*logRaw)
+ if err != nil {
+ return nil, err
+ }
+ events = append(events, event)
+ }
+ return events, nil
+}
+
+// ParseNumberChanges is a log parse operation binding the contract event 0x1161d67e3e40d64da0f22f41054120b745a28aa25e65d98d153fbaf4d3195251.
+//
+// Solidity: event numberChanges(uint256 num)
+func (_Storage *StorageFilterer) ParseNumberChanges(log data.Log) (*StorageNumberChanges, error) {
+ event := new(StorageNumberChanges)
+ if err := _Storage.contract.UnpackLog(event, "numberChanges", log); err != nil {
+ return nil, err
+ }
+ event.Raw = log
+ return event, nil
+}
diff --git a/util/define.go b/util/define.go
index cb34cba..ecdbd1d 100644
--- a/util/define.go
+++ b/util/define.go
@@ -31,3 +31,16 @@ const (
REQUEST_TIMEOUT = 5
DIAL_TIMEOUT = 2
)
+
+const (
+ SchemaAdd = "schema_add"
+ SchemaDel = "schema_del"
+)
+const (
+ OpTypeSchemaAdd = 1
+ OpTypeSchemaDel = 2
+)
+
+const (
+ Seqinterval = 20
+)
diff --git a/util/util.go b/util/util.go
index 080fbea..8dd92f7 100644
--- a/util/util.go
+++ b/util/util.go
@@ -35,11 +35,9 @@ func SignPlainData(privateKey string, data string) (string, error) {
return "", err
}
sequenceZero := uint32(0)
- private := key.Private(&sequenceZero)
hash := crypto.Sha512Half([]byte(data))
- sigBytes, err := crypto.Sign(private, hash, nil)
+ sigBytes, err := crypto.Sign(key, hash, &sequenceZero, nil)
if err != nil {
- log.Println(err)
return "", err
}
return crypto.B2H(sigBytes), nil