diff --git a/.ci/check-commit.sh b/.ci/check-commit.sh index dd36f0eb..353a0a05 100755 --- a/.ci/check-commit.sh +++ b/.ci/check-commit.sh @@ -39,7 +39,9 @@ execute_cmd() { } function check_codeFormat() { - go get golang.org/x/tools/cmd/goimports + go version + go install golang.org/x/tools/cmd/goimports@latest || true + go get golang.org/x/tools/cmd/goimports || true sum=0 for file in $(git diff-index --name-status HEAD^ | grep -v D | grep -E '\.go' | awk '{print $2}'); do checkResult=$(eval "${check_script} ${file}") @@ -103,4 +105,4 @@ function check_PR_limit() { } check_codeFormat -check_PR_limit +# check_PR_limit diff --git a/.ci/counter/Counter.sol b/.ci/counter/Counter.sol index e3525ca7..4b895678 100644 --- a/.ci/counter/Counter.sol +++ b/.ci/counter/Counter.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.24; +pragma solidity>=0.4.24 <0.6.11; contract Counter { int256 public version; diff --git a/.ci/ethPrecompiled/bn256.go b/.ci/ethPrecompiled/bn256.go index 51e55653..cdeed984 100644 --- a/.ci/ethPrecompiled/bn256.go +++ b/.ci/ethPrecompiled/bn256.go @@ -18,7 +18,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/client" "github.com/FISCO-BCOS/go-sdk/conf" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" @@ -33,7 +32,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // BN256ABI is the input ABI used to generate the binding from. diff --git a/.ci/ethPrecompiled/bn256_gm.go b/.ci/ethPrecompiled/bn256_gm.go index 9f02436f..de1f6411 100644 --- a/.ci/ethPrecompiled/bn256_gm.go +++ b/.ci/ethPrecompiled/bn256_gm.go @@ -18,7 +18,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/client" "github.com/FISCO-BCOS/go-sdk/conf" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" @@ -33,7 +32,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // BN256ABI is the input ABI used to generate the binding from. diff --git a/.ci/generate_coverage.sh b/.ci/generate_coverage.sh index 34d48c21..ce3128ac 100644 --- a/.ci/generate_coverage.sh +++ b/.ci/generate_coverage.sh @@ -14,8 +14,9 @@ LOG_INFO() { calculate_coverage() { # start blockchain demo - curl -LO https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/master/tools/build_chain.sh && chmod u+x build_chain.sh - bash build_chain.sh -v 2.5.0 -l 127.0.0.1:4 -o nodes + latest_version=$(curl -sS https://gitee.com/api/v5/repos/FISCO-BCOS/FISCO-BCOS/tags | grep -oe "\"name\":\"v[2-9]*\.[0-9]*\.[0-9]*\"" | cut -d \" -f 4 | sort -V | tail -n 1) + curl -#LO https://github.com/FISCO-BCOS/FISCO-BCOS/releases/download/"${latest_version}"/build_chain.sh && chmod u+x build_chain.sh + bash build_chain.sh -l 127.0.0.1:4 -o nodes cp nodes/127.0.0.1/sdk/* ./ bash nodes/127.0.0.1/start_all.sh diff --git a/.ci/hello/HelloWorld.go b/.ci/hello/HelloWorld.go index 7decd955..eb528ecb 100644 --- a/.ci/hello/HelloWorld.go +++ b/.ci/hello/HelloWorld.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,14 +23,13 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // HelloWorldABI is the input ABI used to generate the binding from. -const HelloWorldABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"v\",\"type\":\"string\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"\",\"type\":\"string\"}],\"name\":\"setValue\",\"type\":\"event\"}]" +const HelloWorldABI = "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"v\",\"type\":\"string\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"setValue\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"v\",\"type\":\"string\"}],\"name\":\"set\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" // HelloWorldBin is the compiled bytecode used for deploying new contracts. -var HelloWorldBin = "0x60806040526040805190810160405280600181526020017f31000000000000000000000000000000000000000000000000000000000000008152506001908051906020019061004f9291906100ae565b5034801561005c57600080fd5b506040805190810160405280600d81526020017f48656c6c6f2c20576f726c642100000000000000000000000000000000000000815250600090805190602001906100a89291906100ae565b50610153565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100ef57805160ff191683800117855561011d565b8280016001018555821561011d579182015b8281111561011c578251825591602001919060010190610101565b5b50905061012a919061012e565b5090565b61015091905b8082111561014c576000816000905550600101610134565b5090565b90565b6104ac806101626000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634ed3885e1461005c57806354fd4d50146100c55780636d4ce63c14610155575b600080fd5b34801561006857600080fd5b506100c3600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101e5565b005b3480156100d157600080fd5b506100da61029b565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561011a5780820151818401526020810190506100ff565b50505050905090810190601f1680156101475780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561016157600080fd5b5061016a610339565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101aa57808201518184015260208101905061018f565b50505050905090810190601f1680156101d75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101fb9291906103db565b507f93a093529f9c8a0c300db4c55fcd27c068c4f5e0e8410bc288c7e76f3d71083e816040518080602001828103825283818151815260200191508051906020019080838360005b8381101561025e578082015181840152602081019050610243565b50505050905090810190601f16801561028b5780820380516001836020036101000a031916815260200191505b509250505060405180910390a150565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103315780601f1061030657610100808354040283529160200191610331565b820191906000526020600020905b81548152906001019060200180831161031457829003601f168201915b505050505081565b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103d15780601f106103a6576101008083540402835291602001916103d1565b820191906000526020600020905b8154815290600101906020018083116103b457829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061041c57805160ff191683800117855561044a565b8280016001018555821561044a579182015b8281111561044957825182559160200191906001019061042e565b5b509050610457919061045b565b5090565b61047d91905b80821115610479576000816000905550600101610461565b5090565b905600a165627a7a72305820fd433a091cb8e1aba3f49e5efb35f937e4b22a85a46f35574834d120699d7ae50029" +var HelloWorldBin = "0x60c060405260016080819052603160f81b60a0908152610020919081610068565b5034801561002d57600080fd5b5060408051808201909152600d8082526c48656c6c6f2c20576f726c642160981b602090920191825261006291600091610068565b50610103565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a957805160ff19168380011785556100d6565b828001600101855582156100d6579182015b828111156100d65782518255916020019190600101906100bb565b506100e29291506100e6565b5090565b61010091905b808211156100e257600081556001016100ec565b90565b6103bd806101126000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80634ed3885e1461004657806354fd4d50146100b85780636d4ce63c14610135575b600080fd5b6100b66004803603602081101561005c57600080fd5b81019060208101813564010000000081111561007757600080fd5b82018360208201111561008957600080fd5b803590602001918460018302840111640100000000831117156100ab57600080fd5b50909250905061013d565b005b6100c06101cb565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fa5781810151838201526020016100e2565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100c0610258565b610149600083836102ef565b50336001600160a01b0316326001600160a01b03167f1cede41e194608a414a2e1d67987cf390338e67d0ff22be86dee2f3737c23d538484600160405180806020018381526020018281038252858582818152602001925080828437600083820152604051601f909101601f1916909201829003965090945050505050a35050565b60018054604080516020600284861615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156102505780601f1061022557610100808354040283529160200191610250565b820191906000526020600020905b81548152906001019060200180831161023357829003601f168201915b505050505081565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156102e45780601f106102b9576101008083540402835291602001916102e4565b820191906000526020600020905b8154815290600101906020018083116102c757829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103305782800160ff1982351617855561035d565b8280016001018555821561035d579182015b8281111561035d578235825591602001919060010190610342565b5061036992915061036d565b5090565b6102ec91905b80821115610369576000815560010161037356fea26469706673582212204a80e82479e39c85b30da9acea9566e1ab9da8ef6873f4502fe3d33297f3398564736f6c634300060a0033" // DeployHelloWorld deploys a new contract, binding an instance of HelloWorld to it. func DeployHelloWorld(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *HelloWorld, error) { @@ -47,6 +45,19 @@ func DeployHelloWorld(auth *bind.TransactOpts, backend bind.ContractBackend) (co return address, tx, &HelloWorld{HelloWorldCaller: HelloWorldCaller{contract: contract}, HelloWorldTransactor: HelloWorldTransactor{contract: contract}, HelloWorldFilterer: HelloWorldFilterer{contract: contract}}, nil } +func AsyncDeployHelloWorld(auth *bind.TransactOpts, handler func(*types.Receipt, error), backend bind.ContractBackend) (*types.Transaction, error) { + parsed, err := abi.JSON(strings.NewReader(HelloWorldABI)) + if err != nil { + return nil, err + } + + tx, err := bind.AsyncDeployContract(auth, handler, parsed, common.FromHex(HelloWorldBin), backend) + if err != nil { + return nil, err + } + return tx, nil +} + // HelloWorld is an auto generated Go binding around a Solidity contract. type HelloWorld struct { HelloWorldCaller // Read-only binding to the contract @@ -248,6 +259,10 @@ func (_HelloWorld *HelloWorldTransactor) Set(opts *bind.TransactOpts, v string) return _HelloWorld.contract.Transact(opts, "set", v) } +func (_HelloWorld *HelloWorldTransactor) AsyncSet(handler func(*types.Receipt, error), opts *bind.TransactOpts, v string) (*types.Transaction, error) { + return _HelloWorld.contract.AsyncTransact(opts, handler, "set", v) +} + // Set is a paid mutator transaction binding the contract method 0x4ed3885e. // // Solidity: function set(string v) returns() @@ -255,6 +270,10 @@ func (_HelloWorld *HelloWorldSession) Set(v string) (*types.Transaction, *types. return _HelloWorld.Contract.Set(&_HelloWorld.TransactOpts, v) } +func (_HelloWorld *HelloWorldSession) AsyncSet(handler func(*types.Receipt, error), v string) (*types.Transaction, error) { + return _HelloWorld.Contract.AsyncSet(handler, &_HelloWorld.TransactOpts, v) +} + // Set is a paid mutator transaction binding the contract method 0x4ed3885e. // // Solidity: function set(string v) returns() @@ -262,131 +281,33 @@ func (_HelloWorld *HelloWorldTransactorSession) Set(v string) (*types.Transactio return _HelloWorld.Contract.Set(&_HelloWorld.TransactOpts, v) } -// HelloWorldSetValueIterator is returned from FilterSetValue and is used to iterate over the raw logs and unpacked data for SetValue events raised by the HelloWorld contract. -type HelloWorldSetValueIterator struct { - Event *HelloWorldSetValue // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.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 *HelloWorldSetValueIterator) 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(HelloWorldSetValue) - 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(HelloWorldSetValue) - 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 *HelloWorldSetValueIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *HelloWorldSetValueIterator) Close() error { - it.sub.Unsubscribe() - return nil +func (_HelloWorld *HelloWorldTransactorSession) AsyncSet(handler func(*types.Receipt, error), v string) (*types.Transaction, error) { + return _HelloWorld.Contract.AsyncSet(handler, &_HelloWorld.TransactOpts, v) } // HelloWorldSetValue represents a SetValue event raised by the HelloWorld contract. type HelloWorldSetValue struct { - Arg0 string - Raw types.Log // Blockchain specific contextual infos + V string + From common.Address + To common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos } -// FilterSetValue is a free log retrieval operation binding the contract event 0x93a093529f9c8a0c300db4c55fcd27c068c4f5e0e8410bc288c7e76f3d71083e. +// WatchSetValue is a free log subscription operation binding the contract event 0x1cede41e194608a414a2e1d67987cf390338e67d0ff22be86dee2f3737c23d53. // -// Solidity: event setValue(string ) -func (_HelloWorld *HelloWorldFilterer) FilterSetValue(opts *bind.FilterOpts) (*HelloWorldSetValueIterator, error) { +// Solidity: event setValue(string v, address indexed from, address indexed to, uint256 value) +func (_HelloWorld *HelloWorldFilterer) WatchSetValue(fromBlock *uint64, handler func(int, []types.Log), from common.Address, to common.Address) error { + return _HelloWorld.contract.WatchLogs(fromBlock, handler, "setValue", from, to) +} - logs, sub, err := _HelloWorld.contract.FilterLogs(opts, "setValue") - if err != nil { - return nil, err - } - return &HelloWorldSetValueIterator{contract: _HelloWorld.contract, event: "setValue", logs: logs, sub: sub}, nil +func (_HelloWorld *HelloWorldFilterer) WatchAllSetValue(fromBlock *uint64, handler func(int, []types.Log)) error { + return _HelloWorld.contract.WatchLogs(fromBlock, handler, "setValue") } -// WatchSetValue is a free log subscription operation binding the contract event 0x93a093529f9c8a0c300db4c55fcd27c068c4f5e0e8410bc288c7e76f3d71083e. +// ParseSetValue is a log parse operation binding the contract event 0x1cede41e194608a414a2e1d67987cf390338e67d0ff22be86dee2f3737c23d53. // -// Solidity: event setValue(string ) -func (_HelloWorld *HelloWorldFilterer) WatchSetValue(opts *bind.WatchOpts, sink chan<- *HelloWorldSetValue) (event.Subscription, error) { - - logs, sub, err := _HelloWorld.contract.WatchLogs(opts, "setValue") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(HelloWorldSetValue) - if err := _HelloWorld.contract.UnpackLog(event, "setValue", 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 -} - -// ParseSetValue is a log parse operation binding the contract event 0x93a093529f9c8a0c300db4c55fcd27c068c4f5e0e8410bc288c7e76f3d71083e. -// -// Solidity: event setValue(string ) +// Solidity: event setValue(string v, address indexed from, address indexed to, uint256 value) func (_HelloWorld *HelloWorldFilterer) ParseSetValue(log types.Log) (*HelloWorldSetValue, error) { event := new(HelloWorldSetValue) if err := _HelloWorld.contract.UnpackLog(event, "setValue", log); err != nil { @@ -394,3 +315,21 @@ func (_HelloWorld *HelloWorldFilterer) ParseSetValue(log types.Log) (*HelloWorld } return event, nil } + +// WatchSetValue is a free log subscription operation binding the contract event 0x1cede41e194608a414a2e1d67987cf390338e67d0ff22be86dee2f3737c23d53. +// +// Solidity: event setValue(string v, address indexed from, address indexed to, uint256 value) +func (_HelloWorld *HelloWorldSession) WatchSetValue(fromBlock *uint64, handler func(int, []types.Log), from common.Address, to common.Address) error { + return _HelloWorld.Contract.WatchSetValue(fromBlock, handler, from, to) +} + +func (_HelloWorld *HelloWorldSession) WatchAllSetValue(fromBlock *uint64, handler func(int, []types.Log)) error { + return _HelloWorld.Contract.WatchAllSetValue(fromBlock, handler) +} + +// ParseSetValue is a log parse operation binding the contract event 0x1cede41e194608a414a2e1d67987cf390338e67d0ff22be86dee2f3737c23d53. +// +// Solidity: event setValue(string v, address indexed from, address indexed to, uint256 value) +func (_HelloWorld *HelloWorldSession) ParseSetValue(log types.Log) (*HelloWorldSetValue, error) { + return _HelloWorld.Contract.ParseSetValue(log) +} diff --git a/.ci/hello/HelloWorld.sol b/.ci/hello/HelloWorld.sol index 86f417f1..6fe15e9a 100644 --- a/.ci/hello/HelloWorld.sol +++ b/.ci/hello/HelloWorld.sol @@ -2,7 +2,7 @@ pragma solidity>=0.4.24 <0.6.11; contract HelloWorld { string value; - event setValue(string); + event setValue(string v, address indexed from, address indexed to, uint256 value); string public version = "1"; constructor() public { @@ -13,8 +13,8 @@ contract HelloWorld { return value; } - function set(string v) public { + function set(string calldata v) public { value = v; - emit setValue(v); + emit setValue(v, tx.origin, msg.sender, 1); } } \ No newline at end of file diff --git a/.ci/integration_test.sh b/.ci/integration_test.sh index 057f5567..9cdf4652 100755 --- a/.ci/integration_test.sh +++ b/.ci/integration_test.sh @@ -2,7 +2,7 @@ set -e -start_time=10 +start_time=15 macOS= check_amop= GOPATH_BIN=$(go env GOPATH)/bin @@ -38,7 +38,8 @@ check_env(){ # export PATH="/usr/local/opt/openssl/bin:$PATH" macOS="macOS" fi - go get -u github.com/sqs/goreturns + go install golang.org/x/tools/cmd/goimports@latest || true + go get golang.org/x/tools/cmd/goimports || true } compile_and_ut() @@ -87,6 +88,21 @@ cat << EOF >> "${output}" fmt.Printf("hello.Get() failed: %v", err) return } + done := make(chan bool) + err = hello.WatchAllSetValue(nil, func(ret int, logs []types.Log) { + fmt.Printf("WatchAllSetValue receive statud: %d, logs: %v\n", ret, logs) + setValue, err := hello.ParseSetValue(logs[0]) + if err != nil { + fmt.Printf("hello.WatchAllSetValue() failed: %v", err) + panic("WatchAllSetValue hello.WatchAllSetValue() failed") + } + fmt.Printf("receive setValue: %+v\n", *setValue) + done <- true + }) + if err != nil { + fmt.Printf("hello.WatchAllSetValue() failed: %v", err) + return + } fmt.Printf("Get: %s\n", ret) _, _, err = hello.Set("fisco") if err != nil { @@ -99,9 +115,22 @@ cat << EOF >> "${output}" return } fmt.Printf("Get: %s\n", ret) + <-done + from := common.HexToAddress("0x83309d045a19c44Dc3722D15A6AbD472f95866aC") + hello.WatchSetValue(nil, func(ret int, logs []types.Log) { + fmt.Printf("WatchSetValue receive statud: %d, logs: %+v\n", ret, logs) + setValue, err := hello.ParseSetValue(logs[0]) + if err != nil { + fmt.Printf("hello.WatchSetValue() failed: %v", err) + panic("hello.WatchSetValue() failed") + } + fmt.Printf("WatchSetValue receive setValue: %+v\n", *setValue) + done <- true + }, from, from) + <-done } EOF - "${GOPATH_BIN}"/goreturns -w "${output}" + "${GOPATH_BIN}"/goimports -w "${output}" } generate_counter() { @@ -158,7 +187,7 @@ cat << EOF >> "${output}" } EOF - "${GOPATH_BIN}"/goreturns -w "${output}" + "${GOPATH_BIN}"/goimports -w "${output}" } get_build_chain() @@ -168,7 +197,7 @@ get_build_chain() } precompiled_test(){ -# TODO: consensus test use getSealer first + # TODO: consensus test use getSealer first precompileds=(config cns crud permission) for pkg in ${precompileds[*]}; do go test -v ./precompiled/${pkg} @@ -178,26 +207,29 @@ precompiled_test(){ integration_std() { LOG_INFO "integration_std testing..." - execute_cmd "bash tools/download_solc.sh -v 0.4.25" + execute_cmd "bash tools/download_solc.sh -v 0.6.10" + + bash build_chain.sh -v "${latest_version}" -l 127.0.0.1:2 -o nodes + cp nodes/127.0.0.1/sdk/* ./ + bash nodes/127.0.0.1/start_all.sh && sleep "${start_time}" # abigen std - execute_cmd "./solc-0.4.25 --bin --abi -o .ci/hello .ci/hello/HelloWorld.sol" + execute_cmd "./solc-0.6.10 --bin --abi --optimize -o .ci/hello .ci/hello/HelloWorld.sol" execute_cmd "./abigen --bin .ci/hello/HelloWorld.bin --abi .ci/hello/HelloWorld.abi --type Hello --pkg main --out=hello.go" generate_hello Hello hello.go execute_cmd "go build -o hello hello.go" execute_cmd "go build -o bn256 .ci/ethPrecompiled/bn256.go" LOG_INFO "generate hello.go and build hello done." - bash build_chain.sh -v "${latest_version}" -l 127.0.0.1:2 -o nodes - cp nodes/127.0.0.1/sdk/* ./ - bash nodes/127.0.0.1/start_all.sh && sleep "${start_time}" + precompiled_test + go test -v ./client + ./hello > hello.out if [ -z "$(grep address hello.out)" ];then LOG_ERROR "std deploy hello contract failed." && cat hello.out && exit 1;fi - if [ ! -z "$(./hello | grep failed)" ];then LOG_ERROR "call hello failed." && exit 1;fi + if [ ! -z "$(cat hello.out | grep failed)" ];then LOG_ERROR "call hello failed." && cat hello.out && exit 1;fi # if [ ! -z "$(./bn256 | grep failed)" ];then ./bn256 && LOG_ERROR "call bn256 failed." && exit 1;fi - precompiled_test - go test -v ./client - execute_cmd "./solc-0.4.25 --bin --abi -o .ci/counter .ci/counter/Counter.sol" + + execute_cmd "./solc-0.6.10 --bin --abi --optimize -o .ci/counter .ci/counter/Counter.sol" execute_cmd "./abigen --bin .ci/counter/Counter.bin --abi .ci/counter/Counter.abi --type Counter --pkg main --out=counter.go" generate_counter Counter counter.go execute_cmd "go build -o counter counter.go" @@ -208,27 +240,27 @@ integration_std() fi bash nodes/127.0.0.1/stop_all.sh LOG_INFO "integration_std testing pass." - } integration_gm() { LOG_INFO "integration_gm testing..." - execute_cmd "bash tools/download_solc.sh -v 0.4.25 -g" + execute_cmd "bash tools/download_solc.sh -v 0.6.10 -g" + + bash build_chain.sh -v "${latest_version}" -l 127.0.0.1:2 -g -o nodes_gm + cp -r nodes_gm/127.0.0.1/sdk/* ./ + bash nodes_gm/127.0.0.1/start_all.sh && sleep "${start_time}" + sed -i "s/SMCrypto=false/SMCrypto=true/g" config.toml + sed -i "s#KeyFile=\".ci/0x83309d045a19c44dc3722d15a6abd472f95866ac.pem\"#KeyFile=\".ci/sm2p256v1_0x791a0073e6dfd9dc5e5061aebc43ab4f7aa4ae8b.pem\"#g" config.toml # abigen gm - execute_cmd "./solc-0.4.25-gm --bin --abi --overwrite -o .ci/hello .ci/hello/HelloWorld.sol" + execute_cmd "./solc-0.6.10-gm --bin --abi --overwrite -o .ci/hello .ci/hello/HelloWorld.sol" execute_cmd "./abigen --bin .ci/hello/HelloWorld.bin --abi .ci/hello/HelloWorld.abi --type Hello --pkg main --out=hello_gm.go --smcrypto=true" generate_hello Hello hello_gm.go execute_cmd "go build -o hello_gm hello_gm.go" execute_cmd "go build -o bn256_gm .ci/ethPrecompiled/bn256_gm.go" LOG_INFO "generate hello_gm.go and build hello_gm done." - bash build_chain.sh -v "${latest_version}" -l 127.0.0.1:2 -g -o nodes_gm - cp -r nodes_gm/127.0.0.1/sdk/* ./ - bash nodes_gm/127.0.0.1/start_all.sh && sleep "${start_time}" - sed -i "s/SMCrypto=false/SMCrypto=true/g" config.toml - sed -i "s#KeyFile=\".ci/0x83309d045a19c44dc3722d15a6abd472f95866ac.pem\"#KeyFile=\".ci/sm2p256v1_0x791a0073e6dfd9dc5e5061aebc43ab4f7aa4ae8b.pem\"#g" config.toml if [ -z "$(./hello_gm | grep address)" ];then LOG_ERROR "gm deploy contract failed." && exit 1;fi ./hello_gm > hello.out if [ ! -z "$(grep failed hello.out)" ];then LOG_ERROR "gm call hello_gm failed." && cat hello.out && exit 1;fi @@ -243,13 +275,15 @@ integration_amop() { LOG_INFO "amop unicast testing..." execute_cmd "go build -o subscriber examples/amop/sub/subscriber.go" execute_cmd "go build -o unicast_publisher examples/amop/unicast_pub/publisher.go" - ./unicast_publisher 127.0.0.1:20200 hello & - ./subscriber 127.0.0.1:20201 hello + ./subscriber 127.0.0.1:20201 hello & + sleep 2 + ./unicast_publisher 127.0.0.1:20200 hello LOG_INFO "amop broadcast testing..." execute_cmd "go build -o broadcast_publisher examples/amop/broadcast_pub/publisher.go" - ./broadcast_publisher 127.0.0.1:20200 hello1 & - ./subscriber 127.0.0.1:20201 hello1 + ./subscriber 127.0.0.1:20201 hello1 & + sleep 2 + ./broadcast_publisher 127.0.0.1:20200 hello1 } parse_params() @@ -268,9 +302,13 @@ main() check_env compile_and_ut get_build_chain - integration_std - if [ -z "${macOS}" ];then integration_gm ; fi + if [ -z "${macOS}" ];then # linux + integration_std + integration_gm + else + integration_std + fi } parse_params "$@" diff --git a/.ci/store/Store.go b/.ci/store/Store.go index 5eed4ffd..fc162d0e 100644 --- a/.ci/store/Store.go +++ b/.ci/store/Store.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // StoreABI is the input ABI used to generate the binding from. @@ -286,137 +284,3 @@ func (_Store *StoreTransactorSession) SetItem(key [32]byte, value [32]byte) (*ty func (_Store *StoreTransactorSession) AsyncSetItem(handler func(*types.Receipt, error), key [32]byte, value [32]byte) (*types.Transaction, error) { return _Store.Contract.AsyncSetItem(handler, &_Store.TransactOpts, key, value) } - -// StoreItemSetIterator is returned from FilterItemSet and is used to iterate over the raw logs and unpacked data for ItemSet events raised by the Store contract. -type StoreItemSetIterator struct { - Event *StoreItemSet // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.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 *StoreItemSetIterator) 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(StoreItemSet) - 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(StoreItemSet) - 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 *StoreItemSetIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *StoreItemSetIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// StoreItemSet represents a ItemSet event raised by the Store contract. -type StoreItemSet struct { - Key [32]byte - Value [32]byte - Raw types.Log // Blockchain specific contextual infos -} - -// FilterItemSet is a free log retrieval operation binding the contract event 0xe79e73da417710ae99aa2088575580a60415d359acfad9cdd3382d59c80281d4. -// -// Solidity: event ItemSet(bytes32 key, bytes32 value) -func (_Store *StoreFilterer) FilterItemSet(opts *bind.FilterOpts) (*StoreItemSetIterator, error) { - - logs, sub, err := _Store.contract.FilterLogs(opts, "ItemSet") - if err != nil { - return nil, err - } - return &StoreItemSetIterator{contract: _Store.contract, event: "ItemSet", logs: logs, sub: sub}, nil -} - -// WatchItemSet is a free log subscription operation binding the contract event 0xe79e73da417710ae99aa2088575580a60415d359acfad9cdd3382d59c80281d4. -// -// Solidity: event ItemSet(bytes32 key, bytes32 value) -func (_Store *StoreFilterer) WatchItemSet(opts *bind.WatchOpts, sink chan<- *StoreItemSet) (event.Subscription, error) { - - logs, sub, err := _Store.contract.WatchLogs(opts, "ItemSet") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(StoreItemSet) - if err := _Store.contract.UnpackLog(event, "ItemSet", 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 -} - -// ParseItemSet is a log parse operation binding the contract event 0xe79e73da417710ae99aa2088575580a60415d359acfad9cdd3382d59c80281d4. -// -// Solidity: event ItemSet(bytes32 key, bytes32 value) -func (_Store *StoreFilterer) ParseItemSet(log types.Log) (*StoreItemSet, error) { - event := new(StoreItemSet) - if err := _Store.contract.UnpackLog(event, "ItemSet", log); err != nil { - return nil, err - } - return event, nil -} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..479ecba1 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,70 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master, '*'] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '30 16 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index f9526743..c95d2aa2 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -16,7 +16,9 @@ jobs: fetch-depth: 1 - uses: actions/setup-go@v2 with: - go-version: 1.14 + go-version: 1.17 + - name: mod tidy + run: go mod tidy - name: generate code coverage report run: bash .ci/generate_coverage.sh - name: upload code coverage to Codecov diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 842350c2..cfa7de9a 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -21,7 +21,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - go: ["1.13", "1.14", "1.15"] + go: ["1.13", "1.15", "1.17", "1.18"] steps: - uses: actions/checkout@v2 with: @@ -41,6 +41,8 @@ jobs: - name: check commit if: ${{ runner.os == 'Linux' && github.base_ref != 'master' && github.event_name == 'pull_request' }} run: bash .ci/check-commit.sh + - name: mod tidy + run: go mod tidy - name: test all if: ${{ runner.os == 'Linux' }} run: bash .ci/integration_test.sh -a diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..5e151626 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,9 @@ +# This configuration file was automatically generated by Gitpod. +# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) +# and commit this file to your remote git repository to share the goodness with others. + +tasks: + - init: go mod tidy + command: go build ./cmd/abigen + + diff --git a/abi/bind/backend.go b/abi/bind/backend.go index 74bf9a0e..8f09e052 100644 --- a/abi/bind/backend.go +++ b/abi/bind/backend.go @@ -87,15 +87,9 @@ type ContractTransactor interface { // 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 + // SubscribeEventLogs 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) + SubscribeEventLogs(eventLogParams types.EventLogParams, handler func(int, []types.Log)) error } // DeployBackend wraps the operations needed by WaitMined and WaitDeployed. diff --git a/abi/bind/base.go b/abi/bind/base.go index 1d975d12..09f110fe 100644 --- a/abi/bind/base.go +++ b/abi/bind/base.go @@ -22,10 +22,10 @@ import ( "errors" "fmt" "math/big" + "strconv" "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -319,84 +319,29 @@ func (c *BoundContract) generateSignedTx(opts *TransactOpts, contract *common.Ad return signedTx, 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 := 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 -} - // 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 types.Log, event.Subscription, error) { +func (c *BoundContract) WatchLogs(fromBlock *uint64, handler func(int, []types.Log), name string, query ...interface{}) error { + from := string("latest") // Don't crash on a lazy user - if opts == nil { - opts = new(WatchOpts) + if fromBlock != nil { + from = strconv.FormatUint(*fromBlock, 10) } // Append the event selector to the query parameters and construct the topic set - query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...) + query = append([]interface{}{c.abi.Events[name].ID()}, query...) topics, err := makeTopics(query...) if err != nil { - return nil, nil, err + return err } - // Start the background filtering - logs := make(chan types.Log, 128) - - config := ethereum.FilterQuery{ - Addresses: []common.Address{c.address}, + eventLogParams := types.EventLogParams{ + FromBlock: from, + ToBlock: "latest", + Addresses: []string{c.address.Hex()}, Topics: topics, + GroupID: c.transactor.GetGroupID().Text(16), } - if opts.Start != nil { - config.FromBlock = new(big.Int).SetUint64(*opts.Start) - } - sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs) - if err != nil { - return nil, nil, err - } - return logs, sub, nil + return c.filterer.SubscribeEventLogs(eventLogParams, handler) } // UnpackLog unpacks a retrieved log into the provided output structure. diff --git a/abi/bind/bind.go b/abi/bind/bind.go index 9e2c2911..485b8cac 100644 --- a/abi/bind/bind.go +++ b/abi/bind/bind.go @@ -210,7 +210,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string] } buffer := new(bytes.Buffer) - funcs := map[string]interface{}{ + funcs := template.FuncMap{ "bindtype": bindType[lang], "bindtopictype": bindTopicType[lang], "namedtype": namedType[lang], diff --git a/abi/bind/template.go b/abi/bind/template.go index a0de46d7..49409a0d 100644 --- a/abi/bind/template.go +++ b/abi/bind/template.go @@ -93,7 +93,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -107,7 +106,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) {{$structs := .Structs}} @@ -382,133 +380,21 @@ var ( {{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 *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.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 types.Log // Blockchain specific contextual infos } - // Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}. - // - // Solidity: {{formatevent .Original $structs}} - func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.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: {{formatevent .Original $structs}} - func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.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}} + func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(fromBlock *uint64, handler func(int, []types.Log){{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} {{bindtype .Type $structs}}{{end}}{{end}}) error { + return _{{$contract.Type}}.contract.WatchLogs(fromBlock, handler, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}{{end}}{{end}}) + } - logs, 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 := <-logs: - // 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) WatchAll{{.Normalized.Name}}(fromBlock *uint64, handler func(int, []types.Log)) error { + return _{{$contract.Type}}.contract.WatchLogs(fromBlock, handler, "{{.Original.Name}}") } // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}. @@ -522,6 +408,24 @@ var ( return event, nil } + // Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{formatevent .Original $structs}} + func (_{{$contract.Type}} *{{$contract.Type}}Session) Watch{{.Normalized.Name}}(fromBlock *uint64, handler func(int, []types.Log){{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} {{bindtype .Type $structs}}{{end}}{{end}}) error { + return _{{$contract.Type}}.Contract.Watch{{.Normalized.Name}}(fromBlock, handler {{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}{{end}}{{end}}) + } + + func (_{{$contract.Type}} *{{$contract.Type}}Session) WatchAll{{.Normalized.Name}}(fromBlock *uint64, handler func(int, []types.Log)) error { + return _{{$contract.Type}}.Contract.WatchAll{{.Normalized.Name}}(fromBlock, handler) + } + + // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}. + // + // Solidity: {{formatevent .Original $structs}} + func (_{{$contract.Type}} *{{$contract.Type}}Session) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) { + return _{{$contract.Type}}.Contract.Parse{{.Normalized.Name}}(log) + } + {{end}} {{end}} ` @@ -716,7 +620,7 @@ const tmplSourceObjc = `// Code generated - DO NOT EDIT. /// {{.Normalized.Name}}{{range .Normalized.Inputs}} /// @param {{.Name}} {{.Type}} type argument{{objcPrintArgComment .Type}}{{end}}{{range .Normalized.Outputs}} /// @return {{.Name}} {{.Type}} type argument{{end}} -- (MobileReceiptResult *) {{.Normalized.Name}} {{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}} +- (MobileReceiptResult *) {{.Normalized.Name}} {{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}} {{.Name}}:{{else}} :{{end}}({{objcFormatStructType .Type $structs $contractName}}) {{.Name}}{{end}}{ {{if .Normalized.Inputs}}NSArray * __resArr = @[ {{range $i, $_ :=.Normalized.Inputs}}{{if ne $i 0}}, @@ -729,7 +633,7 @@ const tmplSourceObjc = `// Code generated - DO NOT EDIT. return [self.sdk sendTransaction:self.abi address:self.address method:@"{{.Original.Name}}" params:__params]; } {{end}} - + - (instancetype)init{ self.abi = @"{{.InputABI}}"; self.bin = @"0x{{.InputBin}}"; @@ -760,7 +664,7 @@ const tmplSourceObjcHeader = `#import #import NS_ASSUME_NONNULL_BEGIN -{{$structs := .Structs}}{{range $contract := .Contracts}}{{$contractName := .Type}} +{{$structs := .Structs}}{{range $contract := .Contracts}}{{$contractName := .Type}} {{range $structs}}// {{.Name}} is an auto generated low-level Go binding around an user-defined struct. @interface {{$contractName}}_{{.Name}} : NSObject {{range $field := .Fields}}@property (nonatomic, assign) {{$field.Type}} {{$field.Name}}; @@ -783,14 +687,14 @@ NS_ASSUME_NONNULL_BEGIN /// deploy {{range .Constructor.Inputs}} /// @param {{.Name}} {{.Type}} type argument{{objcPrintArgComment .Type}}{{end}}{{range .Constructor.Outputs}} /// @return {{.Name}} {{.Type}} type argument{{end}} -- (MobileReceiptResult*) deploy {{range $i, $_ := .Constructor.Inputs}}{{if ne $i 0}} +- (MobileReceiptResult*) deploy {{range $i, $_ := .Constructor.Inputs}}{{if ne $i 0}} {{.Name}}:{{else}} :{{end}}({{objcFormatStructType .Type $structs $contractName}}) {{.Name}}{{end}}; {{end}} {{range .Calls}} /// {{.Normalized.Name}}{{range .Normalized.Inputs}} /// @param {{.Name}} {{.Type}} type argument{{objcPrintArgComment .Type}}{{end}} {{range .Normalized.Outputs}} /// @return {{.Name}} {{.Type}} type argument{{end}} -- (MobileCallResult *) {{.Normalized.Name}} {{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}} +- (MobileCallResult *) {{.Normalized.Name}} {{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}} {{.Name}}:{{else}} :{{end}}({{objcFormatStructType .Type $structs $contractName}}) {{.Name}}{{end}}; {{end}} @@ -798,11 +702,11 @@ NS_ASSUME_NONNULL_BEGIN /// {{.Normalized.Name}}{{range .Normalized.Inputs}} /// @param {{.Name}} {{.Type}} type argument{{objcPrintArgComment .Type}}{{end}}{{range .Normalized.Outputs}} /// @return {{.Name}} {{.Type}} type argument{{end}} -- (MobileReceiptResult *) {{.Normalized.Name}} {{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}} +- (MobileReceiptResult *) {{.Normalized.Name}} {{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}} {{.Name}}:{{else}} :{{end}}({{objcFormatStructType .Type $structs $contractName}}) {{.Name}}{{end}}; {{end}} @end {{end}} NS_ASSUME_NONNULL_END -` \ No newline at end of file +` diff --git a/abi/bind/topics.go b/abi/bind/topics.go index edcf48f8..bca53037 100644 --- a/abi/bind/topics.go +++ b/abi/bind/topics.go @@ -29,76 +29,74 @@ import ( ) // 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: - blob := big.NewInt(int64(rule)).Bytes() - copy(topic[common.HashLength-len(blob):], blob) - case int16: - blob := big.NewInt(int64(rule)).Bytes() - copy(topic[common.HashLength-len(blob):], blob) - case int32: - blob := big.NewInt(int64(rule)).Bytes() - copy(topic[common.HashLength-len(blob):], blob) - case int64: - blob := big.NewInt(rule).Bytes() - copy(topic[common.HashLength-len(blob):], blob) - 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[:]) +func makeTopics(query ...interface{}) ([]string, error) { + topics := make([]string, len(query)) + for i, rule := range query { + 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: + blob := big.NewInt(int64(rule)).Bytes() + copy(topic[common.HashLength-len(blob):], blob) + case int16: + blob := big.NewInt(int64(rule)).Bytes() + copy(topic[common.HashLength-len(blob):], blob) + case int32: + blob := big.NewInt(int64(rule)).Bytes() + copy(topic[common.HashLength-len(blob):], blob) + case int64: + blob := big.NewInt(rule).Bytes() + copy(topic[common.HashLength-len(blob):], blob) + 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: - // 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) - } + return nil, fmt.Errorf("unsupported indexed type: %T", rule) } - topics[i] = append(topics[i], topic) } + topics[i] = topic.Hex() } return topics, nil } diff --git a/client/api_handler.go b/client/api_handler.go index bc836923..c5bd817d 100644 --- a/client/api_handler.go +++ b/client/api_handler.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rlp" + "github.com/sirupsen/logrus" ) // APIHandler defines typed wrappers for the FISCO BCOS RPC API. @@ -108,7 +109,7 @@ func (api *APIHandler) Call(ctx context.Context, groupID int, msg ethereum.CallM func (api *APIHandler) SendRawTransaction(ctx context.Context, groupID int, tx *types.Transaction) (*types.Receipt, error) { data, err := rlp.EncodeToBytes(tx) if err != nil { - fmt.Printf("rlp encode tx error!") + logrus.Printf("rlp encode tx error, err: %v", err) return nil, err } var receipt *types.Receipt @@ -130,7 +131,7 @@ func (api *APIHandler) SendRawTransaction(ctx context.Context, groupID int, tx * if strings.Contains(errorStr, "connection refused") { return nil, err } - //fmt.Printf("Receipt retrieval failed, err: %v\n", err) + //logrus.Printf("Receipt retrieval failed, err: %v\n", err) } else { fmt.Println("Transaction not yet mined") } @@ -164,7 +165,7 @@ func (api *APIHandler) SendRawTransaction(ctx context.Context, groupID int, tx * func (api *APIHandler) AsyncSendRawTransaction(ctx context.Context, groupID int, tx *types.Transaction, handler func(*types.Receipt, error)) error { data, err := rlp.EncodeToBytes(tx) if err != nil { - fmt.Printf("rlp encode tx error!") + logrus.Printf("rlp encode tx error, err: %v", err) return err } if api.IsHTTP() { @@ -209,6 +210,10 @@ func (api *APIHandler) TransactionReceipt(ctx context.Context, groupID int, txHa return r, err } +func (api *APIHandler) SubscribeEventLogs(eventLogParams types.EventLogParams, handler func(int, []types.Log)) error { + return api.Connection.SubscribeEventLogs(eventLogParams, handler) +} + func (api *APIHandler) SubscribeTopic(topic string, handler func([]byte, *[]byte)) error { return api.Connection.SubscribeTopic(topic, handler) } @@ -254,14 +259,13 @@ func (api *APIHandler) UnsubscribeBlockNumberNotify(groupID uint64) error { } // GetClientVersion returns the version of FISCO BCOS running on the nodes. -func (api *APIHandler) GetClientVersion(ctx context.Context) ([]byte, error) { - var raw interface{} - err := api.CallContext(ctx, &raw, "getClientVersion") +func (api *APIHandler) GetClientVersion(ctx context.Context) (*types.ClientVersion, error) { + var clientVersion types.ClientVersion + err := api.CallContext(ctx, &clientVersion, "getClientVersion") if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &clientVersion, err } // GetChainID returns the Chain ID of the FISCO BCOS running on the nodes. @@ -379,25 +383,23 @@ func (api *APIHandler) GetConsensusStatus(ctx context.Context, groupID int) ([]b } // GetSyncStatus returns the synchronization status of the group -func (api *APIHandler) GetSyncStatus(ctx context.Context, groupID int) ([]byte, error) { - var raw interface{} - err := api.CallContext(ctx, &raw, "getSyncStatus", groupID) +func (api *APIHandler) GetSyncStatus(ctx context.Context, groupID int) (*types.SyncStatus, error) { + var syncStatus types.SyncStatus + err := api.CallContext(ctx, &syncStatus, "getSyncStatus", groupID) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &syncStatus, err } // GetPeers returns the information of the connected peers -func (api *APIHandler) GetPeers(ctx context.Context, groupID int) ([]byte, error) { - var raw interface{} - err := api.CallContext(ctx, &raw, "getPeers", groupID) +func (api *APIHandler) GetPeers(ctx context.Context, groupID int) (*[]types.Node, error) { + var nodes []types.Node + err := api.CallContext(ctx, &nodes, "getPeers", groupID) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &nodes, err } // GetGroupPeers returns the nodes and the overser nodes list on a specific group @@ -434,28 +436,26 @@ func (api *APIHandler) GetGroupList(ctx context.Context) ([]byte, error) { } // GetBlockByHash returns the block information according to the given block hash -func (api *APIHandler) GetBlockByHash(ctx context.Context, groupID int, blockHash common.Hash, includeTx bool) ([]byte, error) { - var raw interface{} - err := api.CallContext(ctx, &raw, "getBlockByHash", groupID, blockHash.Hex(), includeTx) +func (api *APIHandler) GetBlockByHash(ctx context.Context, groupID int, blockHash common.Hash, includeTx bool) (*types.Block, error) { + var block types.Block + err := api.CallContext(ctx, &block, "getBlockByHash", groupID, blockHash.Hex(), includeTx) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &block, err } // GetBlockByNumber returns the block information according to the given block number(hex format) -func (api *APIHandler) GetBlockByNumber(ctx context.Context, groupID int, blockNumber int64, includeTx bool) ([]byte, error) { +func (api *APIHandler) GetBlockByNumber(ctx context.Context, groupID int, blockNumber int64, includeTx bool) (*types.Block, error) { + var block types.Block if blockNumber < 0 { return nil, errors.New("Invalid negative block number") } - var raw interface{} - err := api.CallContext(ctx, &raw, "getBlockByNumber", groupID, strconv.FormatInt(blockNumber, 10), includeTx) + err := api.CallContext(ctx, &block, "getBlockByNumber", groupID, strconv.FormatInt(blockNumber, 10), includeTx) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &block, err } // GetBlockHashByNumber returns the block hash according to the given block number @@ -473,41 +473,38 @@ func (api *APIHandler) GetBlockHashByNumber(ctx context.Context, groupID int, bl } // GetTransactionByHash returns the transaction information according to the given transaction hash -func (api *APIHandler) GetTransactionByHash(ctx context.Context, groupID int, txHash common.Hash) ([]byte, error) { - var raw interface{} - err := api.CallContext(ctx, &raw, "getTransactionByHash", groupID, txHash.Hex()) +func (api *APIHandler) GetTransactionByHash(ctx context.Context, groupID int, txHash common.Hash) (*types.TransactionDetail, error) { + var transactionDetail types.TransactionDetail + err := api.CallContext(ctx, &transactionDetail, "getTransactionByHash", groupID, txHash.Hex()) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &transactionDetail, err } // GetTransactionByBlockHashAndIndex returns the transaction information according to // the given block hash and transaction index -func (api *APIHandler) GetTransactionByBlockHashAndIndex(ctx context.Context, groupID int, blockHash common.Hash, txIndex int) ([]byte, error) { - var raw interface{} - err := api.CallContext(ctx, &raw, "getTransactionByBlockHashAndIndex", groupID, blockHash.Hex(), strconv.Itoa(txIndex)) +func (api *APIHandler) GetTransactionByBlockHashAndIndex(ctx context.Context, groupID int, blockHash common.Hash, txIndex int) (*types.TransactionDetail, error) { + var transactionDetail types.TransactionDetail + err := api.CallContext(ctx, &transactionDetail, "getTransactionByBlockHashAndIndex", groupID, blockHash.Hex(), strconv.Itoa(txIndex)) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &transactionDetail, err } // GetTransactionByBlockNumberAndIndex returns the transaction information according to // the given block number and transaction index -func (api *APIHandler) GetTransactionByBlockNumberAndIndex(ctx context.Context, groupID int, blockNumber int64, txIndex int) ([]byte, error) { +func (api *APIHandler) GetTransactionByBlockNumberAndIndex(ctx context.Context, groupID int, blockNumber int64, txIndex int) (*types.TransactionDetail, error) { if blockNumber < 0 { return nil, errors.New("Invalid negative block number") } - var raw interface{} - err := api.CallContext(ctx, &raw, "getTransactionByBlockNumberAndIndex", groupID, strconv.FormatInt(blockNumber, 10), strconv.Itoa(txIndex)) + var transactionDetail types.TransactionDetail + err := api.CallContext(ctx, &transactionDetail, "getTransactionByBlockNumberAndIndex", groupID, strconv.FormatInt(blockNumber, 10), strconv.Itoa(txIndex)) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &transactionDetail, err } // GetTransactionReceipt returns the transaction receipt according to the given transaction hash @@ -559,14 +556,13 @@ func (api *APIHandler) GetContractAddress(ctx context.Context, groupID int, txHa } // GetPendingTransactions returns information of the pending transactions -func (api *APIHandler) GetPendingTransactions(ctx context.Context, groupID int) ([]byte, error) { - var raw interface{} - err := api.CallContext(ctx, &raw, "getPendingTransactions", groupID) +func (api *APIHandler) GetPendingTransactions(ctx context.Context, groupID int) (*[]types.TransactionPending, error) { + var pendingTransactions []types.TransactionPending + err := api.CallContext(ctx, &pendingTransactions, "getPendingTransactions", groupID) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &pendingTransactions, err } // GetPendingTxSize returns amount of the pending transactions @@ -592,14 +588,13 @@ func (api *APIHandler) GetCode(ctx context.Context, groupID int, address common. } // GetTotalTransactionCount returns the total amount of transactions and the block height at present -func (api *APIHandler) GetTotalTransactionCount(ctx context.Context, groupID int) ([]byte, error) { - var raw interface{} - err := api.CallContext(ctx, &raw, "getTotalTransactionCount", groupID) +func (api *APIHandler) GetTotalTransactionCount(ctx context.Context, groupID int) (*types.TransactionCount, error) { + var transactionCount types.TransactionCount + err := api.CallContext(ctx, &transactionCount, "getTotalTransactionCount", groupID) if err != nil { return nil, err } - js, err := json.MarshalIndent(raw, "", indent) - return js, err + return &transactionCount, err } // GetSystemConfigByKey returns value according to the key(only tx_count_limit, tx_gas_limit could work) diff --git a/client/go_client.go b/client/go_client.go index c807dc11..06e81c08 100644 --- a/client/go_client.go +++ b/client/go_client.go @@ -17,10 +17,9 @@ package client import ( "context" "crypto/ecdsa" - "encoding/json" "errors" "fmt" - "log" + "io/ioutil" "math/big" "strconv" "strings" @@ -33,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/sirupsen/logrus" ) // Client defines typed wrappers for the Ethereum RPC API. @@ -63,32 +63,43 @@ func DialContext(ctx context.Context, config *conf.Config) (*Client, error) { if config.IsHTTP { c, err = conn.DialContextHTTP(config.NodeURL) } else { - c, err = conn.DialContextChannel(config.NodeURL, config.CAFile, config.Cert, config.Key, config.GroupID) + // try to parse use file + if config.TLSCAContext == nil { + config.TLSCAContext, err = ioutil.ReadFile(config.CAFile) + if err != nil { + return nil, fmt.Errorf("parse tls root certificate %v failed, err:%v", config.CAFile, err) + } + } + if config.TLSCertContext == nil { + config.TLSCertContext, err = ioutil.ReadFile(config.Cert) + if err != nil { + return nil, fmt.Errorf("parse tls certificate %v failed, err:%v", config.Cert, err) + } + } + if config.TLSKeyContext == nil { + config.TLSKeyContext, err = ioutil.ReadFile(config.Key) + if err != nil { + return nil, fmt.Errorf("parse tls key %v failed, err:%v", config.Key, err) + } + } + c, err = conn.DialContextChannel(config.NodeURL, config.TLSCAContext, config.TLSCertContext, config.TLSKeyContext, config.GroupID) } if err != nil { return nil, err } apiHandler := NewAPIHandler(c) - var response []byte - response, err = apiHandler.GetClientVersion(ctx) + + cv, err := apiHandler.GetClientVersion(ctx) if err != nil { return nil, fmt.Errorf("%v", err) } - var raw interface{} - err = json.Unmarshal(response, &raw) - if err != nil { - return nil, fmt.Errorf("DialContext errors, unmarshal []byte to interface{} failed: %v", err) - } - m, ok := raw.(map[string]interface{}) - if !ok { - return nil, errors.New("parse response json to map error") - } // get supported FISCO BCOS version var compatibleVersionStr string - compatibleVersionStr, ok = m["Supported Version"].(string) - if !ok { + if cv.GetSupportedVersion() == "" { return nil, errors.New("JSON response does not contains the key : Supported Version") + } else { + compatibleVersionStr = cv.GetSupportedVersion() } compatibleVersion, err := getVersionNumber(compatibleVersionStr) if err != nil { @@ -97,9 +108,10 @@ func DialContext(ctx context.Context, config *conf.Config) (*Client, error) { // determine whether FISCO-BCOS Version is consistent with SMCrypto configuration item var fiscoBcosVersion string - fiscoBcosVersion, ok = m["FISCO-BCOS Version"].(string) - if !ok { + if cv.SupportedVersion == "" { return nil, errors.New("JSON response does not contains the key : FISCO-BCOS Version") + } else { + fiscoBcosVersion = cv.GetFiscoBcosVersion() } nodeIsSupportedSM := strings.Contains(fiscoBcosVersion, "gm") || strings.Contains(fiscoBcosVersion, "GM") if nodeIsSupportedSM != config.IsSMCrypto { @@ -108,7 +120,7 @@ func DialContext(ctx context.Context, config *conf.Config) (*Client, error) { // get node chain ID var nodeChainID int64 - nodeChainID, err = strconv.ParseInt(m["Chain Id"].(string), 10, 64) + nodeChainID, err = strconv.ParseInt(cv.GetChainId(), 10, 64) if err != nil { return nil, errors.New("JSON response does not contains the key : Chain Id") } @@ -122,7 +134,7 @@ func DialContext(ctx context.Context, config *conf.Config) (*Client, error) { } else { privateKey, err := crypto.ToECDSA(config.PrivateKey) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } client.auth = bind.NewKeyedTransactor(privateKey) } @@ -172,26 +184,6 @@ func toBlockNumArg(number *big.Int) string { return hexutil.EncodeBig(number) } -// FilterLogs executes a filter query. -func (c *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { - var result []types.Log - arg, err := toFilterArg(q) - if err != nil { - return nil, err - } - err = c.apiHandler.CallContext(ctx, &result, "eth_getLogs", arg) - return result, err -} - -// SubscribeFilterLogs subscribes to the results of a streaming filter query. -func (c *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { - arg, err := toFilterArg(q) - if err != nil { - return nil, err - } - return c.apiHandler.EthSubscribe(ctx, ch, "logs", arg) -} - func toFilterArg(q ethereum.FilterQuery) (interface{}, error) { arg := map[string]interface{}{ "address": q.Addresses, @@ -252,6 +244,10 @@ func (c *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*t return c.apiHandler.GetTransactionReceipt(ctx, c.groupID, txHash) } +func (c *Client) SubscribeEventLogs(eventLogParams types.EventLogParams, handler func(int, []types.Log)) error { + return c.apiHandler.SubscribeEventLogs(eventLogParams, handler) +} + func (c *Client) SubscribeTopic(topic string, handler func([]byte, *[]byte)) error { return c.apiHandler.SubscribeTopic(topic, handler) } @@ -312,7 +308,7 @@ func (c *Client) GetCompatibleVersion() int { } // GetClientVersion returns the version of FISCO BCOS running on the nodes. -func (c *Client) GetClientVersion(ctx context.Context) ([]byte, error) { +func (c *Client) GetClientVersion(ctx context.Context) (*types.ClientVersion, error) { return c.apiHandler.GetClientVersion(ctx) } @@ -357,12 +353,12 @@ func (c *Client) GetConsensusStatus(ctx context.Context) ([]byte, error) { } // GetSyncStatus returns the synchronization status of the group -func (c *Client) GetSyncStatus(ctx context.Context) ([]byte, error) { +func (c *Client) GetSyncStatus(ctx context.Context) (*types.SyncStatus, error) { return c.apiHandler.GetSyncStatus(ctx, c.groupID) } // GetPeers returns the information of the connected peers -func (c *Client) GetPeers(ctx context.Context) ([]byte, error) { +func (c *Client) GetPeers(ctx context.Context) (*[]types.Node, error) { return c.apiHandler.GetPeers(ctx, c.groupID) } @@ -382,12 +378,12 @@ func (c *Client) GetGroupList(ctx context.Context) ([]byte, error) { } // GetBlockByHash returns the block information according to the given block hash -func (c *Client) GetBlockByHash(ctx context.Context, blockHash common.Hash, includeTx bool) ([]byte, error) { +func (c *Client) GetBlockByHash(ctx context.Context, blockHash common.Hash, includeTx bool) (*types.Block, error) { return c.apiHandler.GetBlockByHash(ctx, c.groupID, blockHash, includeTx) } // GetBlockByNumber returns the block information according to the given block number(hex format) -func (c *Client) GetBlockByNumber(ctx context.Context, blockNumber int64, includeTx bool) ([]byte, error) { +func (c *Client) GetBlockByNumber(ctx context.Context, blockNumber int64, includeTx bool) (*types.Block, error) { return c.apiHandler.GetBlockByNumber(ctx, c.groupID, blockNumber, includeTx) } @@ -397,19 +393,19 @@ func (c *Client) GetBlockHashByNumber(ctx context.Context, blockNumber int64) (* } // GetTransactionByHash returns the transaction information according to the given transaction hash -func (c *Client) GetTransactionByHash(ctx context.Context, txHash common.Hash) ([]byte, error) { +func (c *Client) GetTransactionByHash(ctx context.Context, txHash common.Hash) (*types.TransactionDetail, error) { return c.apiHandler.GetTransactionByHash(ctx, c.groupID, txHash) } // GetTransactionByBlockHashAndIndex returns the transaction information according to // the given block hash and transaction index -func (c *Client) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, txIndex int) ([]byte, error) { +func (c *Client) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, txIndex int) (*types.TransactionDetail, error) { return c.apiHandler.GetTransactionByBlockHashAndIndex(ctx, c.groupID, blockHash, txIndex) } // GetTransactionByBlockNumberAndIndex returns the transaction information according to // the given block number and transaction index -func (c *Client) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber int64, txIndex int) ([]byte, error) { +func (c *Client) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNumber int64, txIndex int) (*types.TransactionDetail, error) { return c.apiHandler.GetTransactionByBlockNumberAndIndex(ctx, c.groupID, blockNumber, txIndex) } @@ -424,7 +420,7 @@ func (c *Client) GetContractAddress(ctx context.Context, txHash common.Hash) (co } // GetPendingTransactions returns information of the pending transactions -func (c *Client) GetPendingTransactions(ctx context.Context) ([]byte, error) { +func (c *Client) GetPendingTransactions(ctx context.Context) (*[]types.TransactionPending, error) { return c.apiHandler.GetPendingTransactions(ctx, c.groupID) } @@ -439,7 +435,7 @@ func (c *Client) GetCode(ctx context.Context, address common.Address) ([]byte, e } // GetTotalTransactionCount returns the total amount of transactions and the block height at present -func (c *Client) GetTotalTransactionCount(ctx context.Context) ([]byte, error) { +func (c *Client) GetTotalTransactionCount(ctx context.Context) (*types.TransactionCount, error) { return c.apiHandler.GetTotalTransactionCount(ctx, c.groupID) } diff --git a/client/go_client_test.go b/client/go_client_test.go index c87d7df4..7a8e7f0e 100644 --- a/client/go_client_test.go +++ b/client/go_client_test.go @@ -3,6 +3,8 @@ package client import ( "context" "encoding/hex" + "encoding/json" + "fmt" "strings" "testing" @@ -58,7 +60,8 @@ func TestBlockHashByNumber(t *testing.T) { if err != nil { t.Fatalf("block not found: %v", err) } - t.Logf("block by hash:\n%+v", block) + peers, err := json.MarshalIndent(block, "", indent) + t.Logf("block by hash:\n%+v", peers) _, err = c.GetTransactionByBlockHashAndIndex(context.Background(), *blockHash, 0) if err != nil { t.Fatalf("GetTransactionByBlockHashAndIndex failed: %v", err) @@ -68,21 +71,29 @@ func TestBlockHashByNumber(t *testing.T) { t.Fatalf("transaction receipt not found: %v", err) } t.Logf("transaction receipt by transaction hash:\n%s", raw) - tx, err := c.GetTransactionByHash(context.Background(), *txHash) + transaction, err := c.GetTransactionByHash(context.Background(), *txHash) if err != nil { t.Fatalf("transaction not found: %v", err) } + tx, err := json.MarshalIndent(transaction, "", indent) + if err != nil { + fmt.Printf("transaction marshalIndent error: %v\n", err) + return + } t.Logf("transaction by hash:\n%+v", tx) } func TestClientVersion(t *testing.T) { c := GetClient(t) - cv, err := c.GetClientVersion(context.Background()) + clientVersion, err := c.GetClientVersion(context.Background()) if err != nil { t.Fatalf("client version not found: %v", err) } - + cv, err := json.MarshalIndent(clientVersion, "", indent) + if err != nil { + t.Fatalf("client version marshalIndent error: %v", err) + } t.Logf("client version:\n%s", cv) } @@ -172,22 +183,28 @@ func TestConsensusStatus(t *testing.T) { func TestSyncStatus(t *testing.T) { c := GetClient(t) - raw, err := c.GetSyncStatus(context.Background()) + syncStatus, err := c.GetSyncStatus(context.Background()) if err != nil { t.Fatalf("synchronization status not found: %v", err) } - + raw, err := json.MarshalIndent(syncStatus, "", indent) + if err != nil { + t.Fatalf("synchronization status marshalIndent error: %v", err) + } t.Logf("synchronization Status:\n%s", raw) } func TestPeers(t *testing.T) { c := GetClient(t) - raw, err := c.GetPeers(context.Background()) + nodes, err := c.GetPeers(context.Background()) if err != nil { t.Fatalf("peers not found: %v", err) } - + raw, err := json.MarshalIndent(nodes, "", indent) + if err != nil { + t.Fatalf("peers marshalIndent error: %v", err) + } t.Logf("peers:\n%s", raw) } @@ -228,11 +245,14 @@ func TestBlockByNumber(t *testing.T) { var blockNumber int64 = 1 includeTx := true - raw, err := c.GetBlockByNumber(context.Background(), blockNumber, includeTx) + block, err := c.GetBlockByNumber(context.Background(), blockNumber, includeTx) if err != nil { t.Fatalf("block not found: %v", err) } - + raw, err := json.MarshalIndent(block, "", indent) + if err != nil { + t.Fatalf("peers marshalIndent error: %v", err) + } t.Logf("block by number:\n%s", raw) } @@ -241,22 +261,28 @@ func TestTransactionByBlockNumberAndIndex(t *testing.T) { var blockNumber int64 = 1 txIndex := 0 - raw, err := c.GetTransactionByBlockNumberAndIndex(context.Background(), blockNumber, txIndex) + transcation, err := c.GetTransactionByBlockNumberAndIndex(context.Background(), blockNumber, txIndex) if err != nil { t.Fatalf("transaction not found: %v", err) } - + raw, err := json.MarshalIndent(transcation, "", indent) + if err != nil { + t.Fatalf("transaction marshalIndent error: %v", err) + } t.Logf("transaction by block number and transaction index:\n%s", raw) } func TestPendingTransactions(t *testing.T) { c := GetClient(t) - raw, err := c.GetPendingTransactions(context.Background()) + pendingTransactions, err := c.GetPendingTransactions(context.Background()) if err != nil { t.Fatalf("pending transactions not found: %v", err) } - + raw, err := json.MarshalIndent(pendingTransactions, "", indent) + if err != nil { + t.Fatalf("pendingTransactions marshalIndent error: %v", err) + } t.Logf("pending transactions:\n%s", raw) } @@ -297,11 +323,14 @@ func TestGetCode(t *testing.T) { func TestTotalTransactionCount(t *testing.T) { c := GetClient(t) - raw, err := c.GetTotalTransactionCount(context.Background()) + totalTransactionCount, err := c.GetTotalTransactionCount(context.Background()) if err != nil { t.Fatalf("transactions not found: %v", err) } - + raw, err := json.MarshalIndent(totalTransactionCount, "", indent) + if err != nil { + t.Fatalf("totalTransactionCount MarshalIndent error: %v", err) + } t.Logf("the total transactions and present block height:\n%s", raw) } diff --git a/cmd/commandline/chain_governance.go b/cmd/commandline/chain_governance.go index cf741818..ddee13d1 100644 --- a/cmd/commandline/chain_governance.go +++ b/cmd/commandline/chain_governance.go @@ -12,8 +12,8 @@ import ( var grantCommitteeMember = &cobra.Command{ Use: "grantCommitteeMember", Short: "[accountAddress] Grant a committee member", - Long: `Grant the permission of committee for a user account, which can add and -delete committee members,modify the weight of the committee members, modify the + Long: `Grant the permission of committee for a user account, which can add and +delete committee members,modify the weight of the committee members, modify the effective voting threshold, add and delete nodes, modify chain configuration items, freeze and unfreeze contracts, freeze and unfreeze accounts,add and cancel operator accounts, and write permissions for user tables. diff --git a/cmd/commandline/commands.go b/cmd/commandline/commands.go index e5b3c1d5..42627398 100644 --- a/cmd/commandline/commands.go +++ b/cmd/commandline/commands.go @@ -2,6 +2,7 @@ package commandline import ( "context" + "encoding/json" "fmt" "log" "os" @@ -15,6 +16,10 @@ import ( var info = ", you can type console help for more information" +const ( + indent = " " +) + // commands // var bashCompletionCmd = &cobra.Command{ // Use: "bashCompletion", @@ -67,7 +72,11 @@ var newAccountCmd = &cobra.Command{ fmt.Printf("client version not found: %v\n", err) return } - fmt.Printf("Client Version: \n%s\n", clientVer) + cv, err := json.MarshalIndent(clientVer, "", indent) + if err != nil { + fmt.Printf("client version marshalIndent error: %v", err) + } + fmt.Printf("Client Version: \n%s\n", cv) }, } @@ -84,7 +93,11 @@ var getClientVersionCmd = &cobra.Command{ fmt.Printf("client version not found: %v\n", err) return } - fmt.Printf("Client Version: \n%s\n", clientVer) + cv, err := json.MarshalIndent(clientVer, "", indent) + if err != nil { + fmt.Printf("client version marshalIndent error: %v", err) + } + fmt.Printf("Client Version: \n%s\n", cv) }, } @@ -167,12 +180,12 @@ var getConsensusStatusCmd = &cobra.Command{ Long: `Returns consensus status information within the specified group.`, Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - consensusStatus, err := RPC.GetConsensusStatus(context.Background()) + raw, err := RPC.GetConsensusStatus(context.Background()) if err != nil { fmt.Printf("consensus status not found: %v\n", err) return } - fmt.Printf("Consensus Status: \n%s\n", consensusStatus) + fmt.Printf("Consensus Status: \n%s\n", raw) }, } @@ -187,7 +200,11 @@ var getSyncStatusCmd = &cobra.Command{ fmt.Printf("synchronization status not found: %v\n", err) return } - fmt.Printf("Synchronization Status: \n%s\n", syncStatus) + raw, err := json.MarshalIndent(syncStatus, "", indent) + if err != nil { + fmt.Printf("synchronization status marshalIndent error: %v", err) + } + fmt.Printf("Synchronization Status: \n%s\n", raw) }, } @@ -197,11 +214,15 @@ var getPeersCmd = &cobra.Command{ Long: `Returns the information of connected p2p nodes.`, Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - peers, err := RPC.GetPeers(context.Background()) + nodes, err := RPC.GetPeers(context.Background()) if err != nil { fmt.Printf("peers not found: %v\n", err) return } + peers, err := json.MarshalIndent(nodes, "", indent) + if err != nil { + fmt.Printf("peers marshalIndent error: %v", err) + } fmt.Printf("Peers: \n%s\n", peers) }, } @@ -247,7 +268,7 @@ var getGroupListCmd = &cobra.Command{ fmt.Printf("group IDs list not found: %v\n", err) return } - fmt.Printf("Group ID List: \n%s\n", peers) + fmt.Printf("Group List: \n%s\n", peers) }, } @@ -289,12 +310,13 @@ For more information please refer: includeTx = _includeTx } - blockHash := common.BytesToHash([]byte(args[0])) - peers, err := RPC.GetBlockByHash(context.Background(), blockHash, includeTx) + blockHash := common.HexToHash(args[0]) + block, err := RPC.GetBlockByHash(context.Background(), blockHash, includeTx) if err != nil { fmt.Printf("block not found: %v\n", err) return } + peers, err := json.MarshalIndent(block, "", indent) fmt.Printf("Block: \n%s\n", peers) }, } @@ -345,7 +367,8 @@ For more information please refer: fmt.Printf("block not found: %v\n", err) return } - fmt.Printf("Block: \n%s\n", block) + js, err := json.MarshalIndent(block, "", indent) + fmt.Printf("Block: \n%s\n", js) }, } @@ -410,12 +433,17 @@ For more information please refer: return } - txHash := common.BytesToHash([]byte(args[0])) - tx, err := RPC.GetTransactionByHash(context.Background(), txHash) + txHash := common.HexToHash(args[0]) + transaction, err := RPC.GetTransactionByHash(context.Background(), txHash) if err != nil { fmt.Printf("transaction not found: %v\n", err) return } + tx, err := json.MarshalIndent(transaction, "", indent) + if err != nil { + fmt.Printf("transaction marshalIndent error: %v\n", err) + return + } fmt.Printf("Transaction: \n%s\n", tx) }, } @@ -443,17 +471,22 @@ For more information please refer: return } - txIndex, err := strconv.ParseInt(args[0], 0, 0) + txIndex, err := strconv.ParseInt(args[1], 0, 0) if err != nil { fmt.Printf("parse txIndex failed, please check your input: %s: %v", args[1], err) return } - blockHash := common.BytesToHash([]byte(args[0])) - tx, err := RPC.GetTransactionByBlockHashAndIndex(context.Background(), blockHash, int(txIndex)) + blockHash := common.HexToHash(args[0]) + transaction, err := RPC.GetTransactionByBlockHashAndIndex(context.Background(), blockHash, int(txIndex)) if err != nil { fmt.Printf("transaction not found: %v\n", err) return } + tx, err := json.MarshalIndent(transaction, "", indent) + if err != nil { + fmt.Printf("transaction marshalIndent error: %v\n", err) + return + } fmt.Printf("Transaction: \n%s\n", tx) }, } @@ -497,7 +530,11 @@ For more information please refer: fmt.Printf("transaction not found: %v\n", err) return } - fmt.Printf("Transaction: \n%s\n", tx) + raw, err := json.MarshalIndent(tx, "", indent) + if err != nil { + fmt.Printf("transaction marshalIndent error: %v", err) + } + fmt.Printf("Transaction: \n%s\n", raw) }, } @@ -523,7 +560,7 @@ For more information please refer: return } - txHash := common.BytesToHash([]byte(args[0])) + txHash := common.HexToHash(args[0]) tx, err := RPC.GetTransactionReceipt(context.Background(), txHash) if err != nil { fmt.Printf("transaction receipt not found: %v\n", err) @@ -544,7 +581,7 @@ var getPendingTransactionsCmd = &cobra.Command{ fmt.Printf("transaction not found: %v\n", err) return } - fmt.Printf("Pending Transactions: \n%s\n", tx) + fmt.Printf("Pending Transactions: \n%+v\n", *tx) }, } @@ -587,7 +624,7 @@ For more information please refer: return } - contractAdd := common.BytesToAddress([]byte(args[0])) + contractAdd := common.HexToAddress(args[0]) code, err := RPC.GetCode(context.Background(), contractAdd) if err != nil { fmt.Printf("This address does not exist: %v\n", err) @@ -614,7 +651,11 @@ var getTotalTransactionCountCmd = &cobra.Command{ fmt.Printf("information not found: %v\n", err) return } - fmt.Printf("Latest Statistics on Transaction and Block Height: \n%s\n", counts) + raw, err := json.MarshalIndent(counts, "", indent) + if err != nil { + fmt.Printf("totalTransactionCount MarshalIndent error: %v", err) + } + fmt.Printf("Latest Statistics on Transaction and Block Height: \n%s\n", raw) }, } @@ -712,6 +753,7 @@ $ yourprogram completion fish > ~/.config/fish/completions/yourprogram.fish func init() { // add common command rootCmd.AddCommand(completionCmd) + rootCmd.AddCommand(newAccountCmd) // add node command rootCmd.AddCommand(getClientVersionCmd, getGroupIDCmd, getBlockNumberCmd, getPbftViewCmd, getSealerListCmd) rootCmd.AddCommand(getObserverListCmd, getConsensusStatusCmd, getSyncStatusCmd, getPeersCmd, getGroupPeersCmd) diff --git a/cmd/commandline/contract_life_cycle.go b/cmd/commandline/contract_life_cycle.go index d0e327a4..70d43ac4 100644 --- a/cmd/commandline/contract_life_cycle.go +++ b/cmd/commandline/contract_life_cycle.go @@ -14,7 +14,7 @@ var freezeContract = &cobra.Command{ Short: "[contractAddress] Freeze the contract", Long: `Freeze the contract. Arguments: - [contractAddress]: + [contractAddress]: For example: @@ -53,7 +53,7 @@ var unfreezeContract = &cobra.Command{ Short: "[contractAddress] Unfreeze the contract", Long: `Unfreeze the contract. Arguments: - [contractAddress]: + [contractAddress]: For example: @@ -92,8 +92,8 @@ var grantContractStatusManager = &cobra.Command{ Short: "[contractAddress] [accountAddress] Grant contract authorization to the user", Long: `Grant contract authorization to the user. Arguments: - [contractAddress]: - [accountAddress]: + [contractAddress]: + [accountAddress]: For example: @@ -141,7 +141,7 @@ var getContractStatus = &cobra.Command{ Short: "[contractAddress] Get the status of the contract", Long: `Get the status of the contract, whether the contract is frozen. Arguments: - [contractAddress]: + [contractAddress]: For example: @@ -180,7 +180,7 @@ var listContractStatusManager = &cobra.Command{ Short: "[contractAddress] List the authorization of the contract", Long: `List managers that have the permission to manage contract. Arguments: - [contractAddress]: + [contractAddress]: For example: diff --git a/conf/config.go b/conf/config.go index c149a162..9a68b97d 100644 --- a/conf/config.go +++ b/conf/config.go @@ -4,24 +4,28 @@ package conf import ( "bytes" "fmt" - "log" + "io/ioutil" "os" "strings" + "github.com/sirupsen/logrus" "github.com/spf13/viper" ) // Config contains configuration items for sdk type Config struct { - IsHTTP bool - ChainID int64 - CAFile string - Key string - Cert string - IsSMCrypto bool - PrivateKey []byte - GroupID int - NodeURL string + IsHTTP bool + ChainID int64 + CAFile string + TLSCAContext []byte + Key string + TLSKeyContext []byte + Cert string + TLSCertContext []byte + IsSMCrypto bool + PrivateKey []byte + GroupID int + NodeURL string } // ParseConfigFile parses the configuration from toml config file @@ -34,7 +38,7 @@ func ParseConfigFile(cfgFile string) ([]Config, error) { defer func() { err = file.Close() if err != nil { - log.Fatalf("close file failed, err: %v", err) + logrus.Fatalf("close file failed, err: %v", err) } }() @@ -56,17 +60,20 @@ func ParseConfigFile(cfgFile string) ([]Config, error) { // ParseConfig parses the configuration from []byte func ParseConfig(buffer []byte) ([]Config, error) { viper.SetConfigType("toml") + viper.SetDefault("SMCrypto", false) + viper.SetDefault("Network.Type", "rpc") + viper.SetDefault("Network.CAFile", "ca.crt") + viper.SetDefault("Network.Key", "sdk.key") + viper.SetDefault("Network.Cert", "sdk.crt") + viper.SetDefault("Network.CAContext", "") + viper.SetDefault("Network.KeyContext", "") + viper.SetDefault("Network.CertContext", "") err := viper.ReadConfig(bytes.NewBuffer(buffer)) if err != nil { return nil, fmt.Errorf("viper .ReadConfig failed, err: %v", err) } config := new(Config) var configs []Config - viper.SetDefault("SMCrypto", false) - viper.SetDefault("Network.Type", "rpc") - viper.SetDefault("Network.CAFile", "ca.crt") - viper.SetDefault("Network.Key", "sdk.key") - viper.SetDefault("Network.Cert", "sdk.crt") if viper.IsSet("Chain") { if viper.IsSet("Chain.ChainID") { @@ -105,11 +112,32 @@ func ParseConfig(buffer []byte) ([]Config, error) { } else if strings.EqualFold(connectionType, "channel") { config.IsHTTP = false } else { - fmt.Printf("Network.Type %s is not supported, use channel", connectionType) + logrus.Printf("Network.Type %s is not supported, use channel", connectionType) } config.CAFile = viper.GetString("Network.CAFile") config.Key = viper.GetString("Network.Key") config.Cert = viper.GetString("Network.Cert") + config.TLSCAContext = []byte(viper.GetString("Network.CAContext")) + config.TLSKeyContext = []byte(viper.GetString("Network.KeyContext")) + config.TLSCertContext = []byte(viper.GetString("Network.CertContext")) + if len(config.TLSCAContext) == 0 { + config.TLSCAContext, err = ioutil.ReadFile(config.CAFile) + if err != nil { + panic(err) + } + } + if len(config.TLSKeyContext) == 0 { + config.TLSKeyContext, err = ioutil.ReadFile(config.Key) + if err != nil { + panic(err) + } + } + if len(config.TLSCertContext) == 0 { + config.TLSCertContext, err = ioutil.ReadFile(config.Cert) + if err != nil { + panic(err) + } + } var connections []struct { GroupID int NodeURL string diff --git a/conf/config_test.go b/conf/config_test.go index 9cee4634..74ac600c 100644 --- a/conf/config_test.go +++ b/conf/config_test.go @@ -6,8 +6,83 @@ import ( ) const ( - standardJSON = "[{\"IsHTTP\":false,\"ChainID\":1,\"CAFile\":\"ca.crt\",\"Key\":\"sdk.key\",\"Cert\":\"sdk.crt\",\"IsSMCrypto\":false,\"PrivateKey\":\"uJ1C8SKQBw8jX7j7Ydz5bjsRUWxdT2Mz8m5Ju5Vfi2I=\",\"GroupID\":1,\"NodeURL\":\"127.0.0.1:20200\"}]" - fileContent = "[Network]\n#type rpc or channel\nType=\"channel\"\nCAFile=\"ca.crt\"\nCert=\"sdk.crt\"\nKey=\"sdk.key\"\n[[Network.Connection]]\nNodeURL=\"127.0.0.1:20200\"\nGroupID=1\n# [[Network.Connection]]\n# NodeURL=\"127.0.0.1:20200\"\n# GroupID=2\n\n[Account]\n# only support PEM format for now\nKeyFile=\"../.ci/0x83309d045a19c44dc3722d15a6abd472f95866ac.pem\"\n\n[Chain]\nChainID=1\nSMCrypto=false\n\n[log]\nPath=\"./\"" + standardJSON = `[{"IsHTTP":false,"ChainID":1,"CAFile":"ca.crt","TLSCAContext":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCglNSUlCWkRDQ0FRb0NGQjkvUzViTDZySkY2cUJpSEdmSU9wejJJZDVYTUFvR0NDcUdTTTQ5QkFNQ01EVXhEakFNCglCZ05WQkFNTUJXTm9ZV2x1TVJNd0VRWURWUVFLREFwbWFYTmpieTFpWTI5ek1RNHdEQVlEVlFRTERBVmphR0ZwCgliakFnRncweU1qQTBNVFV3TnpNM05ESmFHQTh5TVRJeU1ETXlNakEzTXpjME1sb3dOVEVPTUF3R0ExVUVBd3dGCglZMmhoYVc0eEV6QVJCZ05WQkFvTUNtWnBjMk52TFdKamIzTXhEakFNQmdOVkJBc01CV05vWVdsdU1GWXdFQVlICglLb1pJemowQ0FRWUZLNEVFQUFvRFFnQUVmdWZLdUJpdE1uMmdpVW5qUVpEUldaRkwwSDdxdUg5d25IVkY2cG9NCglDUEFCbDVRV3lpTEpsM0FTZ3N2YktIMDFZSmY3VG5OZ3FXVmMybVltVXQyNjVEQUtCZ2dxaGtqT1BRUURBZ05JCglBREJGQWlFQTlxdWl0aDg0ZFdyTWtkMlhJVUluVVZRMzZtc1h4bDZjeUVVRkw0c1JrNjRDSUN4MnRWK3JNMHFkCglsNEdEUG1RSmxsQWtrVm55cmU4TFFBYms5dnpuYkZrZAoJLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQoJ","Key":"sdk.key","TLSKeyContext":"LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCglNSUdFQWdFQU1CQUdCeXFHU000OUFnRUdCU3VCQkFBS0JHMHdhd0lCQVFRZ0FRcUJheWF1TUlIRDM2NEVyMlZuCglvblFGUGFXdG44T1p5dThxMjZGRWNHV2hSQU5DQUFTMDVuaE0vdzU2YTg2SVg5TVl4SWZTd2lQR1BoZUdEeXQ3CgluelFNMFZNWWd2dHZ2RUpPQzduNC91RDYreWlUSGI2R1JFb0R5aDRRekdVTEdRaEE0NnJmCgktLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCgk=","Cert":"sdk.crt","TLSCertContext":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCglNSUlCZ2pDQ0FTbWdBd0lCQWdJVVp2ZGVlMERISjVGRXA1VG8vTkprbmorNTRpWXdDZ1lJS29aSXpqMEVBd0l3CglOekVQTUEwR0ExVUVBd3dHWVdkbGJtTjVNUk13RVFZRFZRUUtEQXBtYVhOamJ5MWlZMjl6TVE4d0RRWURWUVFMCglEQVpoWjJWdVkza3dJQmNOTWpJd05ERTFNRGN6TnpReVdoZ1BNakV5TWpBek1qSXdOek0zTkRKYU1ERXhEREFLCglCZ05WQkFNTUEzTmthekVUTUJFR0ExVUVDZ3dLWm1selkyOHRZbU52Y3pFTU1Bb0dBMVVFQ3d3RGMyUnJNRll3CglFQVlIS29aSXpqMENBUVlGSzRFRUFBb0RRZ0FFdE9aNFRQOE9lbXZPaUYvVEdNU0gwc0lqeGo0WGhnOHJlNTgwCglETkZUR0lMN2I3eENUZ3U1K1A3Zyt2c29reDIraGtSS0E4b2VFTXhsQ3hrSVFPT3EzNk1hTUJnd0NRWURWUjBUCglCQUl3QURBTEJnTlZIUThFQkFNQ0JlQXdDZ1lJS29aSXpqMEVBd0lEUndBd1JBSWdBNm42UHNJdFJPWkxmYzF6CglCNS9uWVIraXlyVWdycTg4YzdHVnhZVmliUmdDSUg2QStuT1Fna1Q3Z0dSQlZ0WVVoaFpObEhNem51ZnNhck9PCglzMUhHVURESgoJLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQoJLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCglNSUlCZXpDQ0FTR2dBd0lCQWdJVWR4UGNWTDJ2T05HV25HcFRPZk9td1ZwVUx2Z3dDZ1lJS29aSXpqMEVBd0l3CglOVEVPTUF3R0ExVUVBd3dGWTJoaGFXNHhFekFSQmdOVkJBb01DbVpwYzJOdkxXSmpiM014RGpBTUJnTlZCQXNNCglCV05vWVdsdU1CNFhEVEl5TURReE5UQTNNemMwTWxvWERUTXlNRFF4TWpBM016YzBNbG93TnpFUE1BMEdBMVVFCglBd3dHWVdkbGJtTjVNUk13RVFZRFZRUUtEQXBtYVhOamJ5MWlZMjl6TVE4d0RRWURWUVFMREFaaFoyVnVZM2t3CglWakFRQmdjcWhrak9QUUlCQmdVcmdRUUFDZ05DQUFRU0FReEhHWm14c2tyL2VDbUNyMmtGL1Y0VkZhaG1GZWpqCgk2bURuK3BHYVE1MHFGT3NienRnamtzdkFCOUNDeGplZzFyNUpVV2NKZ25vREhQV1pwSHFMb3hBd0RqQU1CZ05WCglIUk1FQlRBREFRSC9NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUUNNOUZhZ0ViMTU3eGhrU3IvVzIvYVM4b1M3CglicnN6L3NsRzBndFZGTW1SYUFJZ0dhWHMwQk56Wm1ZUDZibm5TUGVzbkJCUWtlS25yL1pSY0F4Z2JVTDdOR1k9CgktLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCgktLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KCU1JSUJaRENDQVFvQ0ZCOS9TNWJMNnJKRjZxQmlIR2ZJT3B6MklkNVhNQW9HQ0NxR1NNNDlCQU1DTURVeERqQU0KCUJnTlZCQU1NQldOb1lXbHVNUk13RVFZRFZRUUtEQXBtYVhOamJ5MWlZMjl6TVE0d0RBWURWUVFMREFWamFHRnAKCWJqQWdGdzB5TWpBME1UVXdOek0zTkRKYUdBOHlNVEl5TURNeU1qQTNNemMwTWxvd05URU9NQXdHQTFVRUF3d0YKCVkyaGhhVzR4RXpBUkJnTlZCQW9NQ21acGMyTnZMV0pqYjNNeERqQU1CZ05WQkFzTUJXTm9ZV2x1TUZZd0VBWUgKCUtvWkl6ajBDQVFZRks0RUVBQW9EUWdBRWZ1Zkt1Qml0TW4yZ2lVbmpRWkRSV1pGTDBIN3F1SDl3bkhWRjZwb00KCUNQQUJsNVFXeWlMSmwzQVNnc3ZiS0gwMVlKZjdUbk5ncVdWYzJtWW1VdDI2NURBS0JnZ3Foa2pPUFFRREFnTkkKCUFEQkZBaUVBOXF1aXRoODRkV3JNa2QyWElVSW5VVlEzNm1zWHhsNmN5RVVGTDRzUms2NENJQ3gydFYrck0wcWQKCWw0R0RQbVFKbGxBa2tWbnlyZThMUUFiazl2em5iRmtkCgktLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCgk=","IsSMCrypto":false,"PrivateKey":"uJ1C8SKQBw8jX7j7Ydz5bjsRUWxdT2Mz8m5Ju5Vfi2I=","GroupID":1,"NodeURL":"127.0.0.1:20200"}]` + fileContent = ` + [Network] + #type rpc or channel + Type="channel" + CAFile="ca.crt" + Cert="sdk.crt" + Key="sdk.key" + # if the certificate context is not empty, use it, otherwise read from the certificate file + CAContext='''-----BEGIN CERTIFICATE----- + MIIBZDCCAQoCFB9/S5bL6rJF6qBiHGfIOpz2Id5XMAoGCCqGSM49BAMCMDUxDjAM + BgNVBAMMBWNoYWluMRMwEQYDVQQKDApmaXNjby1iY29zMQ4wDAYDVQQLDAVjaGFp + bjAgFw0yMjA0MTUwNzM3NDJaGA8yMTIyMDMyMjA3Mzc0MlowNTEOMAwGA1UEAwwF + Y2hhaW4xEzARBgNVBAoMCmZpc2NvLWJjb3MxDjAMBgNVBAsMBWNoYWluMFYwEAYH + KoZIzj0CAQYFK4EEAAoDQgAEfufKuBitMn2giUnjQZDRWZFL0H7quH9wnHVF6poM + CPABl5QWyiLJl3ASgsvbKH01YJf7TnNgqWVc2mYmUt265DAKBggqhkjOPQQDAgNI + ADBFAiEA9quith84dWrMkd2XIUInUVQ36msXxl6cyEUFL4sRk64CICx2tV+rM0qd + l4GDPmQJllAkkVnyre8LQAbk9vznbFkd + -----END CERTIFICATE----- + ''' + KeyContext='''-----BEGIN PRIVATE KEY----- + MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgAQqBayauMIHD364Er2Vn + onQFPaWtn8OZyu8q26FEcGWhRANCAAS05nhM/w56a86IX9MYxIfSwiPGPheGDyt7 + nzQM0VMYgvtvvEJOC7n4/uD6+yiTHb6GREoDyh4QzGULGQhA46rf + -----END PRIVATE KEY----- + ''' + CertContext='''-----BEGIN CERTIFICATE----- + MIIBgjCCASmgAwIBAgIUZvdee0DHJ5FEp5To/NJknj+54iYwCgYIKoZIzj0EAwIw + NzEPMA0GA1UEAwwGYWdlbmN5MRMwEQYDVQQKDApmaXNjby1iY29zMQ8wDQYDVQQL + DAZhZ2VuY3kwIBcNMjIwNDE1MDczNzQyWhgPMjEyMjAzMjIwNzM3NDJaMDExDDAK + BgNVBAMMA3NkazETMBEGA1UECgwKZmlzY28tYmNvczEMMAoGA1UECwwDc2RrMFYw + EAYHKoZIzj0CAQYFK4EEAAoDQgAEtOZ4TP8OemvOiF/TGMSH0sIjxj4Xhg8re580 + DNFTGIL7b7xCTgu5+P7g+vsokx2+hkRKA8oeEMxlCxkIQOOq36MaMBgwCQYDVR0T + BAIwADALBgNVHQ8EBAMCBeAwCgYIKoZIzj0EAwIDRwAwRAIgA6n6PsItROZLfc1z + B5/nYR+iyrUgrq88c7GVxYVibRgCIH6A+nOQgkT7gGRBVtYUhhZNlHMznufsarOO + s1HGUDDJ + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBezCCASGgAwIBAgIUdxPcVL2vONGWnGpTOfOmwVpULvgwCgYIKoZIzj0EAwIw + NTEOMAwGA1UEAwwFY2hhaW4xEzARBgNVBAoMCmZpc2NvLWJjb3MxDjAMBgNVBAsM + BWNoYWluMB4XDTIyMDQxNTA3Mzc0MloXDTMyMDQxMjA3Mzc0MlowNzEPMA0GA1UE + AwwGYWdlbmN5MRMwEQYDVQQKDApmaXNjby1iY29zMQ8wDQYDVQQLDAZhZ2VuY3kw + VjAQBgcqhkjOPQIBBgUrgQQACgNCAAQSAQxHGZmxskr/eCmCr2kF/V4VFahmFejj + 6mDn+pGaQ50qFOsbztgjksvAB9CCxjeg1r5JUWcJgnoDHPWZpHqLoxAwDjAMBgNV + HRMEBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIQCM9FagEb157xhkSr/W2/aS8oS7 + brsz/slG0gtVFMmRaAIgGaXs0BNzZmYP6bnnSPesnBBQkeKnr/ZRcAxgbUL7NGY= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBZDCCAQoCFB9/S5bL6rJF6qBiHGfIOpz2Id5XMAoGCCqGSM49BAMCMDUxDjAM + BgNVBAMMBWNoYWluMRMwEQYDVQQKDApmaXNjby1iY29zMQ4wDAYDVQQLDAVjaGFp + bjAgFw0yMjA0MTUwNzM3NDJaGA8yMTIyMDMyMjA3Mzc0MlowNTEOMAwGA1UEAwwF + Y2hhaW4xEzARBgNVBAoMCmZpc2NvLWJjb3MxDjAMBgNVBAsMBWNoYWluMFYwEAYH + KoZIzj0CAQYFK4EEAAoDQgAEfufKuBitMn2giUnjQZDRWZFL0H7quH9wnHVF6poM + CPABl5QWyiLJl3ASgsvbKH01YJf7TnNgqWVc2mYmUt265DAKBggqhkjOPQQDAgNI + ADBFAiEA9quith84dWrMkd2XIUInUVQ36msXxl6cyEUFL4sRk64CICx2tV+rM0qd + l4GDPmQJllAkkVnyre8LQAbk9vznbFkd + -----END CERTIFICATE----- + ''' + + [[Network.Connection]] + NodeURL="127.0.0.1:20200" + GroupID=1 + # [[Network.Connection]] + # NodeURL="127.0.0.1:20200" + # GroupID=2 + + [Account] + # only support PEM format for now + KeyFile="../.ci/0x83309d045a19c44dc3722d15a6abd472f95866ac.pem" + + [Chain] + ChainID=1 + SMCrypto=false + + [log] + Path="./" + ` ) func TestConfig(t *testing.T) { diff --git a/config.toml b/config.toml index 494e9e81..5c85f3b5 100644 --- a/config.toml +++ b/config.toml @@ -5,6 +5,12 @@ Type="channel" CAFile="ca.crt" Cert="sdk.crt" Key="sdk.key" +# if the certificate context is not empty, use it, otherwise read from the certificate file +# multi lines use triple quotes +CAContext='''''' +KeyContext='''''' +CertContext='''''' + [[Network.Connection]] NodeURL="127.0.0.1:20200" GroupID=1 diff --git a/conn/channel.go b/conn/channel.go index ff9af89d..d55e81e8 100644 --- a/conn/channel.go +++ b/conn/channel.go @@ -24,18 +24,20 @@ import ( "fmt" "io" "io/ioutil" - "log" "math/big" + "net" "net/http" "strconv" "strings" "sync" "time" - tls "github.com/FISCO-BCOS/crypto/tls" + "github.com/FISCO-BCOS/crypto/tls" "github.com/FISCO-BCOS/go-sdk/core/types" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/google/uuid" + "github.com/sirupsen/logrus" "golang.org/x/crypto/sha3" ) @@ -44,6 +46,8 @@ const ( messageHeaderLength = 42 protocolVersion = 3 clientType = "Go-SDK" + heartBeatInterval = 30 + tlsConnReadDeadline = 10 ) type nodeInfo struct { @@ -52,8 +56,14 @@ type nodeInfo struct { CompatibleVersion string `json:"nodeVersion"` } +type eventInfo struct { + params *types.EventLogParams + handler func(int, []types.Log) +} + type channelSession struct { // groupID uint + connMu sync.Mutex c *tls.Conn mu sync.RWMutex responses map[string]*channelResponse @@ -61,6 +71,8 @@ type channelSession struct { receiptResponses map[string]*channelResponse topicMu sync.RWMutex topicHandlers map[string]func([]byte, *[]byte) + eventLogMu sync.RWMutex + eventLogHandlers map[string]eventInfo asyncMu sync.RWMutex asyncHandlers map[string]func(*types.Receipt, error) blockNotifyMu sync.RWMutex @@ -71,6 +83,7 @@ type channelSession struct { closed chan interface{} endpoint string tlsConfig *tls.Config + heartBeat *time.Ticker } const ( @@ -131,6 +144,23 @@ type channelResponse struct { Notify chan interface{} } +type EventLog struct { + LogIndex string `json:"logIndex"` + TransactionIndex string `json:"transactionIndex"` + TransactionHash string `json:"transactionHash"` + BlockHash string `json:"blockHash"` + BlockNumber string `json:"blockNumber"` + Address string `json:"address"` + Data string `json:"data"` + Topics []string `json:"topics"` +} + +type eventLogResponse struct { + FilterID string `json:"filterID"` + Logs []EventLog `json:"logs"` + Result int `json:"result"` +} + type requestAuth struct { Topic string `json:"topic"` TopicForCert string `json:"topicForCert"` @@ -146,7 +176,7 @@ type updateAuthTopicStatus struct { func newChannelMessage(msgType uint16, body []byte) (*channelMessage, error) { id, err := uuid.NewUUID() if err != nil { - log.Printf("newChannelMessage error: %v", err) + logrus.Warnf("newChannelMessage error: %v", err) return nil, err } idString := strings.ReplaceAll(id.String(), "-", "") @@ -171,16 +201,16 @@ func (t *topicData) Encode() []byte { buf := bytes.NewBuffer(raw) err := binary.Write(buf, binary.LittleEndian, t.length) if err != nil { - log.Fatal("encode length error:", err) + logrus.Fatal("encode length error:", err) } err = binary.Write(buf, binary.LittleEndian, []byte(t.topic)) if err != nil { - log.Fatal("encode type error:", err) + logrus.Fatal("encode type error:", err) } err = binary.Write(buf, binary.LittleEndian, t.data) if err != nil { - log.Fatal("encode data error:", err) + logrus.Fatal("encode data error:", err) } return buf.Bytes() } @@ -190,27 +220,27 @@ func (msg *channelMessage) Encode() []byte { buf := bytes.NewBuffer(raw) err := binary.Write(buf, binary.BigEndian, msg.length) if err != nil { - log.Fatal("encode length error:", err) + logrus.Fatal("encode length error:", err) } err = binary.Write(buf, binary.BigEndian, msg.typeN) if err != nil { - log.Fatal("encode type error:", err) + logrus.Fatal("encode type error:", err) } err = binary.Write(buf, binary.LittleEndian, []byte(msg.uuid)) if err != nil { - log.Fatal("encode uuid error:", err) + logrus.Fatal("encode uuid error:", err) } err = binary.Write(buf, binary.BigEndian, msg.errorCode) if err != nil { - log.Fatal("encode ErrorCode error:", err) + logrus.Fatal("encode ErrorCode error:", err) } err = binary.Write(buf, binary.LittleEndian, msg.body) if err != nil { - log.Fatal("encode Body error:", err) + logrus.Fatal("encode Body error:", err) } if uint32(buf.Len()) != msg.length { - fmt.Printf("%d != %d\n, buf is %v", buf.Len(), msg.length, buf.String()) - log.Fatal("encode error length error:", err) + logrus.Warnf("%d != %d\n, buf is %v", buf.Len(), msg.length, buf.String()) + logrus.Fatal("encode error length error:", err) } return buf.Bytes() } @@ -220,32 +250,32 @@ func decodeChannelMessage(raw []byte) (*channelMessage, error) { result := new(channelMessage) err := binary.Read(buf, binary.BigEndian, &result.length) if err != nil { - fmt.Println("binary.Read failed:", err) + logrus.Println("binary.Read failed:", err) } if uint32(len(raw)) < result.length { return nil, errors.New("uncomplete message") } err = binary.Read(buf, binary.BigEndian, &result.typeN) if err != nil { - fmt.Println("binary.Read failed:", err) + logrus.Println("binary.Read failed:", err) } var uuid [32]byte err = binary.Read(buf, binary.LittleEndian, &uuid) if err != nil { - // log.Fatal("encode error:", err) - fmt.Println("binary.Read failed:", err) + // logrus.Fatal("encode error:", err) + logrus.Println("binary.Read failed:", err) } result.uuid = string(uuid[:]) err = binary.Read(buf, binary.BigEndian, &result.errorCode) if err != nil { - fmt.Println("binary.Read failed:", err) + logrus.Println("binary.Read failed:", err) } dataLength := result.length - messageHeaderLength result.body = make([]byte, dataLength) err = binary.Read(buf, binary.BigEndian, &result.body) if err != nil { - fmt.Println("binary.Read failed:", err) + logrus.Println("binary.Read failed:", err) } return result, nil } @@ -255,19 +285,19 @@ func decodeTopic(raw []byte) (*topicData, error) { result := new(topicData) err := binary.Read(buf, binary.LittleEndian, &result.length) if err != nil { - fmt.Println("binary.Read failed:", err) + logrus.Println("binary.Read failed:", err) } topic := make([]byte, result.length-1) err = binary.Read(buf, binary.LittleEndian, &topic) if err != nil { - fmt.Println("binary.Read failed:", err) + logrus.Println("binary.Read failed:", err) } result.topic = string(topic) dataLength := len(raw) - int(result.length) result.data = make([]byte, dataLength) err = binary.Read(buf, binary.LittleEndian, &result.data) if err != nil { - fmt.Println("binary.Read failed:", err) + logrus.Println("binary.Read failed:", err) } return result, nil } @@ -337,13 +367,15 @@ func DialChannelWithClient(endpoint string, config *tls.Config, groupID int) (*C } ch := &channelSession{c: conn, responses: make(map[string]*channelResponse), receiptResponses: make(map[string]*channelResponse), topicHandlers: make(map[string]func([]byte, *[]byte)), + eventLogHandlers: make(map[string]eventInfo), asyncHandlers: make(map[string]func(*types.Receipt, error)), blockNotifyHandlers: make(map[uint64]func(int64)), nodeInfo: nodeInfo{blockNumber: 0, Protocol: 1}, closed: make(chan interface{}), endpoint: endpoint, - tlsConfig: config} + tlsConfig: config, + heartBeat: time.NewTicker(heartBeatInterval * time.Second)} go ch.processMessages() if err = ch.handshakeChannel(); err != nil { - fmt.Printf("handshake channel protocol failed, use default protocol version") + logrus.Errorf("handshake channel protocol failed, use default protocol version") } ch.topicHandlers[blockNotifyPrefix+strconv.Itoa(groupID)] = nil if err = ch.sendSubscribedTopics(); err != nil { @@ -416,7 +448,6 @@ func (hc *channelSession) doRPCRequest(ctx context.Context, msg interface{}) (io return nil, err } msgBytes := rpcMsg.Encode() - if hc.c == nil { return nil, errors.New("connection unavailable") } @@ -428,7 +459,6 @@ func (hc *channelSession) doRPCRequest(ctx context.Context, msg interface{}) (io hc.mu.Lock() hc.responses[rpcMsg.uuid] = response hc.mu.Unlock() - <-response.Notify hc.mu.Lock() response = hc.responses[rpcMsg.uuid] @@ -491,7 +521,7 @@ func (hc *channelSession) sendTransaction(ctx context.Context, msg interface{}) if respmsg.Error != nil { return nil, fmt.Errorf("send transaction error, code=%d, message=%s", respmsg.Error.Code, respmsg.Error.Message) } - // fmt.Printf("sendTransaction reveived response,seq:%s message:%s\n ", rpcMsg.uuid, respmsg.Result) + // logrus.Warnf("sendTransaction reveived response,seq:%s message:%s\n ", rpcMsg.uuid, respmsg.Result) <-receiptResponse.Notify hc.mu.RLock() receiptResponse = hc.receiptResponses[rpcMsg.uuid] @@ -579,7 +609,9 @@ func (hc *channelSession) sendMessage(msg *channelMessage) (*channelMessage, err if hc.c == nil { return nil, errors.New("connection unavailable") } + hc.connMu.Lock() _, err := hc.c.Write(msgBytes) + hc.connMu.Unlock() if err != nil { return nil, err } @@ -634,7 +666,30 @@ func (hc *channelSession) handshakeChannel() error { return fmt.Errorf("parse handshake channel protocol response failed %w", err) } hc.nodeInfo = info - // fmt.Printf("node info:%+v", info) + // logrus.Warnf("node info:%+v", info) + return nil +} + +func (hc *channelSession) sendSubscribedEvent(eventLogParams *types.EventLogParams) error { + data, err := json.Marshal(*eventLogParams) + if err != nil { + return errors.New("marshal eventLogParams failed") + } + msg, err := newTopicMessage("", data, clientRegisterEventLog) + if err != nil { + return fmt.Errorf("new topic message failed, err: %v", err) + } + response, err := hc.sendMessage(msg) + if err != nil { + return err + } + result := &struct { + Result int `json:"result"` + }{} + json.Unmarshal(response.body, result) + if result.Result != 0 { + return fmt.Errorf("send subscribed event failed, result: %v", result.Result) + } return nil } @@ -654,6 +709,30 @@ func (hc *channelSession) sendSubscribedTopics() error { return hc.sendMessageNoResponse(msg) } +func (hc *channelSession) subscribeEvent(eventLogParams types.EventLogParams, handler func(int, []types.Log)) error { + if handler == nil { + return errors.New("handler is nil") + } + id, err := uuid.NewUUID() + if err != nil { + return errors.New("new UUID failed") + } + eventLogParams.FilterID = strings.ReplaceAll(id.String(), "-", "") + hc.eventLogMu.RLock() + _, ok := hc.eventLogHandlers[eventLogParams.FilterID] + hc.eventLogMu.RUnlock() + if ok { + return errors.New("already subscribed to event " + eventLogParams.FilterID) + } + if err := hc.sendSubscribedEvent(&eventLogParams); err != nil { + return err + } + hc.eventLogMu.Lock() + hc.eventLogHandlers[eventLogParams.FilterID] = eventInfo{&eventLogParams, handler} + hc.eventLogMu.Unlock() + return nil +} + func (hc *channelSession) subscribeTopic(topic string, handler func([]byte, *[]byte)) error { if len(topic) > maxTopicLength { return errors.New("topic length exceeds 254") @@ -698,6 +777,19 @@ func (hc *channelSession) sendAMOPMsg(topic string, data []byte) ([]byte, error) return responseTopicData.data, nil } +func (hc *channelSession) sendHeartbeatMsg() error { + msg, err := newChannelMessage(clientHeartbeat, nil) + if err != nil { + return fmt.Errorf("new topic message failed, err: %v", err) + } + // ignore response, because if wait response will block the process message + err = hc.sendMessageNoResponse(msg) + if err != nil { + return fmt.Errorf("sendMessage failed, err: %v", err) + } + return nil +} + func (hc *channelSession) broadcastAMOPMsg(topic string, data []byte) error { msg, err := newTopicMessage(topic, data, amopMultiCast) if err != nil { @@ -727,22 +819,23 @@ func (hc *channelSession) publishPrivateTopic(topic string, publicKeys []*ecdsa. var authInfo requestAuth err := json.Unmarshal(data, &authInfo) if err != nil { - log.Printf("unmarshal authInfo failed, err: %v", err) + logrus.Warnf("unmarshal authInfo failed, err: %v", err) return } randomData := generateRandomNum() signature, err := hc.sendAMOPMsg(authInfo.TopicForCert, randomData) if err != nil { - log.Printf("send message failed, err: %v", err) + logrus.Warnf("send message failed, err: %v", err) + return } if len(signature) == 0 { - log.Println("signature is empty") + logrus.Println("signature is empty") return } var checkResult = 1 // 1 is false hw := sha3.NewLegacyKeccak256() if _, err = hw.Write(randomData); err != nil { - log.Printf("keccak256 failed, err: %v\n", err) + logrus.Warnf("keccak256 failed, err: %v\n", err) return } digest := hw.Sum(nil) @@ -750,7 +843,7 @@ func (hc *channelSession) publishPrivateTopic(topic string, publicKeys []*ecdsa. publicKeyBytes := crypto.FromECDSAPub(publicKeys[i]) if crypto.VerifySignature(publicKeyBytes, digest, signature[:len(signature)-1]) { checkResult = 0 - // log.Printf("verify NodeID %v success", authInfo.NodeID) + // logrus.Printf("verify NodeID %v success", authInfo.NodeID) break } } @@ -760,15 +853,15 @@ func (hc *channelSession) publishPrivateTopic(topic string, publicKeys []*ecdsa. updateNodeTopicStatus.NodeID = authInfo.NodeID jsonBytes, err := json.Marshal(updateNodeTopicStatus) if err != nil { - log.Printf("nodeUpdateTopicStatus marshal failed, err: %v", err) + logrus.Warnf("nodeUpdateTopicStatus marshal failed, err: %v", err) } newMessage, err := newChannelMessage(amopUpdateTopicStatus, jsonBytes) if err != nil { - log.Printf("new topic message failed, err: %v", err) + logrus.Warnf("new topic message failed, err: %v", err) } err = hc.sendMessageNoResponse(newMessage) if err != nil { - log.Printf("send message no response failed, err: %v", err) + logrus.Warnf("send message no response failed, err: %v", err) } } hc.topicMu.Lock() @@ -802,13 +895,13 @@ func (hc *channelSession) subscribePrivateTopic(topic string, privateKey *ecdsa. // sign random number and send back hw := sha3.NewLegacyKeccak256() if _, err = hw.Write(data); err != nil { - log.Printf("keccak256 failed, err: %v\n", err) + logrus.Warnf("keccak256 failed, err: %v\n", err) return } digest := hw.Sum(nil) signature, err := crypto.Sign(digest, privateKey) if err != nil { - log.Printf("sign random number failed, err: %v\n", err) + logrus.Warnf("sign random number failed, err: %v\n", err) return } *response = signature @@ -850,7 +943,7 @@ func (hc *channelSession) broadcastAMOPPrivateMsg(topic string, data []byte) err func (hc *channelSession) processTopicMessage(msg *channelMessage) { topic, err := decodeTopic(msg.body) if err != nil { - // fmt.Printf("decode topic failed: %+v\n", msg) + // logrus.Warnf("decode topic failed: %+v\n", msg) return } hc.topicMu.RLock() @@ -862,38 +955,37 @@ func (hc *channelSession) processTopicMessage(msg *channelMessage) { } responseMessage, err := newTopicMessage(topic.topic, *responseData, amopResponse) if err != nil { - // fmt.Printf("err: %v\n", err) + logrus.Warnf("newTopicMessage failed, err: %v\n", err) return } responseMessage.uuid = msg.uuid err = hc.sendMessageNoResponse(responseMessage) if err != nil { - // fmt.Println("response message failed") + logrus.Warnf("response message failed, uuid: %v, err: %v\n", msg.uuid, err) return } - // fmt.Printf("unsubscribed topic %s\n", topic.topic) } func (hc *channelSession) processAuthTopicMessage(msg *channelMessage) { var requestAuthInfo requestAuth err := json.Unmarshal(msg.body, &requestAuthInfo) if err != nil { - // fmt.Printf("unmarshal authInfo failed, err: %v\n", err) + // logrus.Warnf("unmarshal authInfo failed, err: %v\n", err) return } responseMessage, err := newTopicMessage(authChannelPrefix+requestAuthInfo.TopicForCert, nil, amopResponse) if err != nil { - // fmt.Printf("err: %v\n", err) + // logrus.Warnf("err: %v\n", err) return } responseMessage.uuid = msg.uuid err = hc.sendMessageNoResponse(responseMessage) if err != nil { - // fmt.Println("response message failed") + // logrus.Println("response message failed") return } - // fmt.Printf("unsubscribed topic %s\n", requestAuthInfo.Topic) + // logrus.Warnf("unsubscribed topic %s\n", requestAuthInfo.Topic) hc.topicMu.RLock() handler, ok := hc.topicHandlers[pushChannelPrefix+requestAuthInfo.Topic] hc.topicMu.RUnlock() @@ -904,11 +996,67 @@ func (hc *channelSession) processAuthTopicMessage(msg *channelMessage) { } } +func hexToUint64(s string) (uint64, error) { + cleaned := strings.Replace(s, "0x", "", -1) + return strconv.ParseUint(cleaned, 16, 64) +} + +func (hc *channelSession) processEventLogMessage(msg *channelMessage) { + var eventLogResponse eventLogResponse + err := json.Unmarshal(msg.body, &eventLogResponse) + if err != nil { + logrus.Warnf("unmarshal eventLogResponse failed, err: %v\n", err) + return + } + logs := []types.Log{} + var nextBlock uint64 + for _, log := range eventLogResponse.Logs { + number, _ := strconv.Atoi(log.BlockNumber) + logIndex, err := hexToUint64(log.LogIndex) + if err != nil { + logrus.Warnf("unmarshal logIndex failed, err: %v\n", err) + return + } + txIndex, err := hexToUint64(log.TransactionIndex) + if err != nil { + logrus.Warnf("unmarshal TransactionIndex failed, err: %v\n", err) + return + } + topics := []common.Hash{} + for _, topic := range log.Topics { + topics = append(topics, common.HexToHash(topic)) + } + data := common.FromHex(log.Data) + logs = append(logs, types.Log{ + Address: common.HexToAddress(log.Address), + Topics: topics, + Data: data, + BlockNumber: uint64(number), + TxHash: common.HexToHash(log.TransactionHash), + TxIndex: uint(txIndex), + BlockHash: common.HexToHash(log.BlockHash), + Index: uint(logIndex), + Removed: false, + }) + nextBlock = uint64(number) + 1 + } + + hc.eventLogMu.RLock() + eventLogInfo, ok := hc.eventLogHandlers[eventLogResponse.FilterID] + if ok { + eventLogInfo.params.FromBlock = strconv.FormatUint(nextBlock, 10) + } + hc.eventLogMu.RUnlock() + if ok { + go eventLogInfo.handler(eventLogResponse.Result, logs) + } +} + +// processMessages process incoming messages from the node func (hc *channelSession) processMessages() { for { select { case <-hc.closed: - // fmt.Println("exit from processMessage") // delete old network _ = hc.c.Close() hc.c = nil @@ -934,6 +1082,8 @@ func (hc *channelSession) processMessages() { for { con, err := tls.Dial("tcp", hc.endpoint, hc.tlsConfig) if err != nil { + logrus.Warnf("tls.Dial %v failed, err: %v\n", hc.endpoint, err) + time.Sleep(5 * time.Second) continue } hc.c = con @@ -942,29 +1092,41 @@ func (hc *channelSession) processMessages() { hc.nodeInfo.Protocol = 1 go hc.processMessages() if err = hc.handshakeChannel(); err != nil { - fmt.Printf("handshake channel protocol failed, use default protocol version") + logrus.Warnf("handshake channel protocol failed, use default protocol version") } err = hc.sendSubscribedTopics() // re-subscribe topic if err != nil { - log.Printf("re-subscriber topic failed") + logrus.Errorf("re-subscriber topic failed, err: %v\n", err) + } + // resubscribe contract event + for _, eventLogInfo := range hc.eventLogHandlers { + err = hc.sendSubscribedEvent(eventLogInfo.params) + if err != nil { + logrus.Errorf("re-subscriber event failed, event: %+v, err: %v\n", *eventLogInfo.params, err) + } } return } + case <-hc.heartBeat.C: + hc.sendHeartbeatMsg() default: receiveBuf := make([]byte, 4096) + hc.c.SetReadDeadline(time.Now().Add(tlsConnReadDeadline * time.Second)) b, err := hc.c.Read(receiveBuf) if err != nil { - // fmt.Printf("channel Read error:%v\n", err) - hc.Close() + nerr, ok := err.(net.Error) + if !ok || !nerr.Timeout() { + logrus.Warnf("channel Read error:%v\n", err) + hc.Close() + } continue } hc.buf = append(hc.buf, receiveBuf[:b]...) msg, err := decodeChannelMessage(hc.buf) if err != nil { - // fmt.Printf("decodeChannelMessage error:%v", err) + logrus.Debugf("decodeChannelMessage error:%v", err) continue } - // fmt.Printf("message %+v\n", msg) hc.buf = hc.buf[msg.length:] // TODO: move notify into switch hc.mu.Lock() @@ -978,10 +1140,9 @@ func (hc *channelSession) processMessages() { } hc.mu.Unlock() switch msg.typeN { - case rpcMessage, amopResponse, clientHandshake: - // fmt.Printf("response type:%d seq:%s, msg:%s", msg.typeN, msg.uuid, string(msg.body)) + case rpcMessage, amopResponse, clientHandshake, clientRegisterEventLog, clientHeartbeat: + logrus.Debugf("response type:%d seq:%s, msg:%s", msg.typeN, msg.uuid, string(msg.body)) case transactionNotify: - // fmt.Printf("transaction notify:%s", string(msg.body)) hc.mu.Lock() if receipt, ok := hc.receiptResponses[msg.uuid]; ok { receipt.Message = msg @@ -1020,9 +1181,11 @@ func (hc *channelSession) processMessages() { go hc.processTopicMessage(msg) case amopAuthTopic: go hc.processAuthTopicMessage(msg) - // fmt.Printf("response type:%d seq:%s, msg:%s, err:%v", msg.typeN, msg.uuid, string(msg.body), err) + // logrus.Printf("response type:%d seq:%s, msg:%s, err:%v", msg.typeN, msg.uuid, string(msg.body), err) + case eventLogPush: + go hc.processEventLogMessage(msg) default: - fmt.Printf("unknown message type:%d, msg:%+v", msg.typeN, msg) + logrus.Errorf("unknown message type:%d, msg:%+v", msg.typeN, msg) } } } @@ -1033,19 +1196,19 @@ func (hc *channelSession) updateBlockNumber(msg *channelMessage) { var groupID uint64 topic, err := decodeTopic(msg.body) if err != nil { - fmt.Printf("decodeTopic msg.body failed, err: %v\n", err) + logrus.Warnf("decodeTopic msg.body failed, err: %v\n", err) return } if hc.nodeInfo.Protocol == 1 { response := strings.Split(string(topic.data), ",") blockNumber, err = strconv.ParseInt(response[1], 10, 32) if err != nil { - fmt.Printf("v1 block notify parse blockNumber failed, %v\n", string(topic.data)) + logrus.Warnf("v1 block notify parse blockNumber failed, %v\n", string(topic.data)) return } groupID, err = strconv.ParseUint(response[0], 10, 32) if err != nil { - fmt.Printf("v1 block notify parse GroupID failed, %v\n", string(topic.data)) + logrus.Warnf("v1 block notify parse GroupID failed, %v\n", string(topic.data)) return } } else { @@ -1055,13 +1218,13 @@ func (hc *channelSession) updateBlockNumber(msg *channelMessage) { } err = json.Unmarshal(topic.data, ¬ify) if err != nil { - fmt.Printf("block notify parse blockNumber failed, %v\n", string(topic.data)) + logrus.Warnf("block notify parse blockNumber failed, %v\n", string(topic.data)) return } blockNumber = notify.BlockNumber groupID = notify.GroupID } - // fmt.Printf("blockNumber updated %d -> %d", hc.nodeInfo.blockNumber, blockNumber) + // logrus.Printf("blockNumber updated %d -> %d", hc.nodeInfo.blockNumber, blockNumber) hc.nodeInfo.blockNumber = blockNumber if handler, ok := hc.blockNotifyHandlers[groupID]; ok { diff --git a/conn/connection.go b/conn/connection.go index 0e1e46ff..5c980407 100644 --- a/conn/connection.go +++ b/conn/connection.go @@ -17,14 +17,10 @@ package conn import ( - "bytes" "context" "crypto/ecdsa" "encoding/json" "errors" - "io/ioutil" - "log" - "reflect" "strconv" "strings" "sync/atomic" @@ -79,10 +75,7 @@ type BatchElem struct { // Connection represents a connection to an RPC server. type Connection struct { - idgen func() ID // for subscriptions - isHTTP bool - services *serviceRegistry - + isHTTP bool idCounter uint32 // This function, if non-nil, is called when the connection is lost. @@ -110,18 +103,14 @@ type reconnectFunc func(ctx context.Context) (ServerCodec, error) type clientContextKey struct{} type clientConn struct { - codec ServerCodec - handler *handler + codec ServerCodec } func (c *Connection) newClientConn(conn ServerCodec) *clientConn { - ctx := context.WithValue(context.Background(), clientContextKey{}, c) - handler := newHandler(ctx, conn, c.idgen, c.services) - return &clientConn{conn, handler} + return &clientConn{conn} } func (cc *clientConn) close(err error, inflightReq *requestOp) { - cc.handler.close(err, inflightReq) cc.codec.Close() } @@ -134,7 +123,6 @@ type requestOp struct { ids []json.RawMessage err error resp chan *jsonrpcMessage // receives up to len(ids) responses - sub *ClientSubscription // only set for EthSubscribe requests } func (op *requestOp) wait(ctx context.Context, c *Connection) (*jsonrpcMessage, error) { @@ -167,19 +155,14 @@ func DialContextHTTP(rawurl string) (*Connection, error) { } // DialContextChannel creates a new Channel client, just like Dial. -func DialContextChannel(rawurl, caFile, certFile, keyFile string, groupID int) (*Connection, error) { +func DialContextChannel(rawurl string, caRoot, certContext, keyContext []byte, groupID int) (*Connection, error) { roots := x509.NewCertPool() - rootPEM, err := ioutil.ReadFile(caFile) - if err != nil { - panic(err) - } - ok := roots.AppendCertsFromPEM([]byte(rootPEM)) + ok := roots.AppendCertsFromPEM(caRoot) if !ok { panic("failed to parse root certificate") } - cer, err := tls.LoadX509KeyPair(certFile, keyFile) + cer, err := tls.X509KeyPair(certContext, keyContext) if err != nil { - // log.Println(err) return nil, err } config := &tls.Config{RootCAs: roots, Certificates: []tls.Certificate{cer}, MinVersion: tls.VersionTLS12, PreferServerCipherSuites: true, @@ -200,17 +183,15 @@ func newClient(initctx context.Context, connect reconnectFunc) (*Connection, err if err != nil { return nil, err } - c := initClient(conn, randomIDGenerator(), new(serviceRegistry)) + c := initClient(conn) c.reconnectFunc = connect return c, nil } -func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *Connection { +func initClient(conn ServerCodec) *Connection { _, isHTTP := conn.(*httpConn) c := &Connection{ - idgen: idgen, isHTTP: isHTTP, - services: services, writeConn: conn, close: make(chan struct{}), closing: make(chan struct{}), @@ -222,36 +203,14 @@ func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *C reqSent: make(chan error, 1), reqTimeout: make(chan *requestOp), } - // FIXME: remove substration releated code - // if !isHTTP { - // go c.dispatch(conn) - // } return c } -// RegisterName creates a service for the given receiver type under the given name. When no -// methods on the given receiver match the criteria to be either a RPC method or a -// subscription an error is returned. Otherwise a new service is created and added to the -// service collection this client provides to the server. -func (c *Connection) RegisterName(name string, receiver interface{}) error { - return c.services.registerName(name, receiver) -} - func (c *Connection) nextID() json.RawMessage { id := atomic.AddUint32(&c.idCounter, 1) return strconv.AppendUint(nil, uint64(id), 10) } -// SupportedModules calls the rpc_modules method, retrieving the list of -// APIs that are available on the server. -func (c *Connection) SupportedModules() (map[string]string, error) { - var result map[string]string - ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) - defer cancel() - err := c.CallContext(ctx, &result, "rpc_modules") - return result, err -} - // Close closes the client, aborting any in-flight requests. func (c *Connection) Close() { if c.isHTTP { @@ -259,12 +218,6 @@ func (c *Connection) Close() { } hc := c.writeConn.(*channelSession) hc.Close() - // FIXME: remove substration releated code - // select { - // case c.close <- struct{}{}: - // <-c.didClose - // case <-c.didClose: - // } } // Call performs a JSON-RPC call with the given arguments and unmarshals into @@ -293,8 +246,6 @@ func (c *Connection) CallContext(ctx context.Context, result interface{}, method err = c.sendHTTP(ctx, op, msg) } else { err = c.sendRPCRequest(ctx, op, msg) - // FIXME: remove substration releated code - // err = c.send(ctx, op, msg) } if err != nil { return err @@ -307,7 +258,7 @@ func (c *Connection) CallContext(ctx context.Context, result interface{}, method case resp.Error != nil: return resp.Error case len(resp.Result) == 0: - // log.Printf("result is null, %+v, err:%+v \n", resp, err) + // logrus.Printf("result is null, %+v, err:%+v \n", resp, err) return ErrNoResult default: return json.Unmarshal(resp.Result, &result) @@ -329,6 +280,11 @@ func (c *Connection) AsyncSendTransaction(ctx context.Context, handler func(*typ return nil } +func (c *Connection) SubscribeEventLogs(eventLogParams types.EventLogParams, handler func(int, []types.Log)) error { + hc := c.writeConn.(*channelSession) + return hc.subscribeEvent(eventLogParams, handler) +} + func (c *Connection) SubscribeTopic(topic string, handler func([]byte, *[]byte)) error { hc := c.writeConn.(*channelSession) return hc.subscribeTopic(topic, handler) @@ -384,150 +340,6 @@ func (c *Connection) UnsubscribeBlockNumberNotify(groupID uint64) error { return hc.unSubscribeBlockNumberNotify(groupID) } -// BatchCall sends all given requests as a single batch and waits for the server -// to return a response for all of them. -// -// In contrast to Call, BatchCall only returns I/O errors. Any error specific to -// a request is reported through the Error field of the corresponding BatchElem. -// -// Note that batch calls may not be executed atomically on the server side. -func (c *Connection) BatchCall(b []BatchElem) error { - ctx := context.Background() - return c.BatchCallContext(ctx, b) -} - -// BatchCallContext sends all given requests as a single batch and waits for the server -// to return a response for all of them. The wait duration is bounded by the -// context's deadline. -// -// In contrast to CallContext, BatchCallContext only returns errors that have occurred -// while sending the request. Any error specific to a request is reported through the -// Error field of the corresponding BatchElem. -// -// Note that batch calls may not be executed atomically on the server side. -func (c *Connection) BatchCallContext(ctx context.Context, b []BatchElem) error { - msgs := make([]*jsonrpcMessage, len(b)) - op := &requestOp{ - ids: make([]json.RawMessage, len(b)), - resp: make(chan *jsonrpcMessage, len(b)), - } - for i, elem := range b { - msg, err := c.newMessage(elem.Method, elem.Args...) - if err != nil { - return err - } - msgs[i] = msg - op.ids[i] = msg.ID - } - - var err error - if c.isHTTP { - err = c.sendBatchHTTP(ctx, op, msgs) - } else { - err = c.send(ctx, op, msgs) - } - - // Wait for all responses to come back. - for n := 0; n < len(b) && err == nil; n++ { - var resp *jsonrpcMessage - resp, err = op.wait(ctx, c) - if err != nil { - break - } - // Find the element corresponding to this response. - // The element is guaranteed to be present because dispatch - // only sends valid IDs to our channel. - var elem *BatchElem - for i := range msgs { - if bytes.Equal(msgs[i].ID, resp.ID) { - elem = &b[i] - break - } - } - if resp.Error != nil { - elem.Error = resp.Error - continue - } - if len(resp.Result) == 0 { - elem.Error = ErrNoResult - continue - } - elem.Error = json.Unmarshal(resp.Result, elem.Result) - } - return err -} - -// Notify sends a notification, i.e. a method call that doesn't expect a response. -func (c *Connection) Notify(ctx context.Context, method string, args ...interface{}) error { - op := new(requestOp) - msg, err := c.newMessage(method, args...) - if err != nil { - return err - } - msg.ID = nil - - if c.isHTTP { - return c.sendHTTP(ctx, op, msg) - } - return c.send(ctx, op, msg) -} - -// EthSubscribe registers a subscripion under the "eth" namespace. -func (c *Connection) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { - return c.Subscribe(ctx, "eth", channel, args...) -} - -// ShhSubscribe registers a subscripion under the "shh" namespace. -func (c *Connection) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { - return c.Subscribe(ctx, "shh", channel, args...) -} - -// Subscribe calls the "_subscribe" method with the given arguments, -// registering a subscription. Server notifications for the subscription are -// sent to the given channel. The element type of the channel must match the -// expected type of content returned by the subscription. -// -// The context argument cancels the RPC request that sets up the subscription but has no -// effect on the subscription after Subscribe has returned. -// -// Slow subscribers will be dropped eventually. Connection buffers up to 20000 notifications -// before considering the subscriber dead. The subscription Err channel will receive -// ErrSubscriptionQueueOverflow. Use a sufficiently large buffer on the channel or ensure -// that the channel usually has at least one reader to prevent this issue. -func (c *Connection) Subscribe(ctx context.Context, namespace string, channel interface{}, args ...interface{}) (*ClientSubscription, error) { - // Check type of channel first. - chanVal := reflect.ValueOf(channel) - if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 { - panic("first argument to Subscribe must be a writable channel") - } - if chanVal.IsNil() { - panic("channel given to Subscribe must not be nil") - } - if c.isHTTP { - return nil, ErrNotificationsUnsupported - } - - msg, err := c.newMessage(namespace+subscribeMethodSuffix, args...) - if err != nil { - return nil, err - } - op := &requestOp{ - ids: []json.RawMessage{msg.ID}, - resp: make(chan *jsonrpcMessage), - sub: newClientSubscription(c, namespace, chanVal), - } - - // Send the subscription request. - // The arrival and validity of the response is signaled on sub.quit. - if err := c.send(ctx, op, msg); err != nil { - return nil, err - } - if _, err := op.wait(ctx, c); err != nil { - return nil, err - } - return op.sub, nil -} - func (c *Connection) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) { msg := &jsonrpcMessage{Version: vsn, ID: c.nextID(), Method: method} if paramsIn != nil { // prevent sending "params":null @@ -539,37 +351,6 @@ func (c *Connection) newMessage(method string, paramsIn ...interface{}) (*jsonrp return msg, nil } -// send registers op with the dispatch loop, then sends msg on the connection. -// if sending fails, op is deregistered. -func (c *Connection) send(ctx context.Context, op *requestOp, msg interface{}) error { - select { - case c.reqInit <- op: - err := c.write(ctx, msg) - c.reqSent <- err - return err - case <-ctx.Done(): - // This can happen if the client is overloaded or unable to keep up with - // subscription notifications. - return ctx.Err() - case <-c.closing: - return ErrClientQuit - } -} - -func (c *Connection) write(ctx context.Context, msg interface{}) error { - // The previous write failed. Try to establish a new connection. - if c.writeConn == nil { - if err := c.reconnect(ctx); err != nil { - return err - } - } - err := c.writeConn.Write(ctx, msg) - if err != nil { - c.writeConn = nil - } - return err -} - func (c *Connection) reconnect(ctx context.Context) error { if c.reconnectFunc == nil { return errDead @@ -582,7 +363,7 @@ func (c *Connection) reconnect(ctx context.Context) error { } newconn, err := c.reconnectFunc(ctx) if err != nil { - // log.Trace("RPC client reconnect failed", "err", err) + // logrus.Trace("RPC client reconnect failed", "err", err) return err } select { @@ -595,88 +376,6 @@ func (c *Connection) reconnect(ctx context.Context) error { } } -// dispatch is the main loop of the client. -// It sends read messages to waiting calls to Call and BatchCall -// and subscription notifications to registered subscriptions. -func (c *Connection) dispatch(codec ServerCodec) { - var ( - lastOp *requestOp // tracks last send operation - reqInitLock = c.reqInit // nil while the send lock is held - conn = c.newClientConn(codec) - reading = true - ) - defer func() { - close(c.closing) - if reading { - conn.close(ErrClientQuit, nil) - c.drainRead() - } - close(c.didClose) - }() - - // Spawn the initial read loop. - go c.read(codec) - - for { - select { - case <-c.close: - return - - // Read path: - case op := <-c.readOp: - if op.batch { - conn.handler.handleBatch(op.msgs) - } else { - conn.handler.handleMsg(op.msgs[0]) - } - - case err := <-c.readErr: - // conn.handler.log.Debug("RPC connection read error", "err", err) - conn.close(err, lastOp) - reading = false - - // Reconnect: - case newcodec := <-c.reconnected: - // log.Debug("RPC client reconnected", "reading", reading, "conn", newcodec.RemoteAddr()) - if reading { - // Wait for the previous read loop to exit. This is a rare case which - // happens if this loop isn't notified in time after the connection breaks. - // In those cases the caller will notice first and reconnect. Closing the - // handler terminates all waiting requests (closing op.resp) except for - // lastOp, which will be transferred to the new handler. - conn.close(errClientReconnected, lastOp) - c.drainRead() - } - go c.read(newcodec) - reading = true - conn = c.newClientConn(newcodec) - // Re-register the in-flight request on the new handler - // because that's where it will be sent. - conn.handler.addRequestOp(lastOp) - - // Send path: - case op := <-reqInitLock: - // Stop listening for further requests until the current one has been sent. - reqInitLock = nil - lastOp = op - conn.handler.addRequestOp(op) - - case err := <-c.reqSent: - if err != nil { - // Remove response handlers for the last send. When the read loop - // goes down, it will signal all other current operations. - conn.handler.removeRequestOp(lastOp) - } - // Let the next request in. - reqInitLock = c.reqInit - lastOp = nil - - case op := <-c.reqTimeout: - conn.handler.removeRequestOp(op) - } - } -} - // drainRead drops read messages until an error occurs. func (c *Connection) drainRead() { for { @@ -688,24 +387,6 @@ func (c *Connection) drainRead() { } } -// read decodes RPC messages from a codec, feeding them into dispatch. -func (c *Connection) read(codec ServerCodec) { - for { - msgs, batch, err := codec.Read() - if _, ok := err.(*json.SyntaxError); ok { - err := codec.Write(context.Background(), errorMessage(&parseError{err.Error()})) - if err != nil { - log.Fatal(err) - } - } - if err != nil { - c.readErr <- err - return - } - c.readOp <- readOp{msgs, batch} - } -} - // IsHTTP returns whether is HTTP func (c *Connection) IsHTTP() bool { return c.isHTTP diff --git a/conn/errors.go b/conn/errors.go deleted file mode 100644 index 22860712..00000000 --- a/conn/errors.go +++ /dev/null @@ -1,65 +0,0 @@ -// 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 conn - -import "fmt" - -const defaultErrorCode = -32000 - -type methodNotFoundError struct{ method string } - -func (e *methodNotFoundError) ErrorCode() int { return -32601 } - -func (e *methodNotFoundError) Error() string { - return fmt.Sprintf("the method %s does not exist/is not available", e.method) -} - -type subscriptionNotFoundError struct{ namespace, subscription string } - -func (e *subscriptionNotFoundError) ErrorCode() int { return -32601 } - -func (e *subscriptionNotFoundError) Error() string { - return fmt.Sprintf("no %q subscription in %s namespace", e.subscription, e.namespace) -} - -// Invalid JSON was received by the server. -type parseError struct{ message string } - -func (e *parseError) ErrorCode() int { return -32700 } - -func (e *parseError) Error() string { return e.message } - -// received message isn't a valid request -type invalidRequestError struct{ message string } - -func (e *invalidRequestError) ErrorCode() int { return -32600 } - -func (e *invalidRequestError) Error() string { return e.message } - -// received message is invalid -type invalidMessageError struct{ message string } - -func (e *invalidMessageError) ErrorCode() int { return -32700 } - -func (e *invalidMessageError) Error() string { return e.message } - -// unable to decode supplied params, or an invalid number of parameters -type invalidParamsError struct{ message string } - -func (e *invalidParamsError) ErrorCode() int { return -32602 } - -func (e *invalidParamsError) Error() string { return e.message } diff --git a/conn/handler.go b/conn/handler.go deleted file mode 100644 index 443147d6..00000000 --- a/conn/handler.go +++ /dev/null @@ -1,406 +0,0 @@ -// 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 conn - -import ( - "context" - "encoding/json" - "log" - "reflect" - "strconv" - "strings" - "sync" - // "time" -) - -// handler handles JSON-RPC messages. There is one handler per connection. Note that -// handler is not safe for concurrent use. Message handling never blocks indefinitely -// because RPCs are processed on background goroutines launched by handler. -// -// The entry points for incoming messages are: -// -// h.handleMsg(message) -// h.handleBatch(message) -// -// Outgoing calls use the requestOp struct. Register the request before sending it -// on the connection: -// -// op := &requestOp{ids: ...} -// h.addRequestOp(op) -// -// Now send the request, then wait for the reply to be delivered through handleMsg: -// -// if err := op.wait(...); err != nil { -// h.removeRequestOp(op) // timeout, etc. -// } -// -type handler struct { - reg *serviceRegistry - unsubscribeCb *callback - idgen func() ID // subscription ID generator - respWait map[string]*requestOp // active client requests - clientSubs map[string]*ClientSubscription // active client subscriptions - callWG sync.WaitGroup // pending call goroutines - rootCtx context.Context // canceled by close() - cancelRoot func() // cancel function for rootCtx - conn jsonWriter // where responses will be sent - allowSubscribe bool - - subLock sync.Mutex - serverSubs map[ID]*Subscription -} - -type callProc struct { - ctx context.Context - notifiers []*Notifier -} - -func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry) *handler { - rootCtx, cancelRoot := context.WithCancel(connCtx) - h := &handler{ - reg: reg, - idgen: idgen, - conn: conn, - respWait: make(map[string]*requestOp), - clientSubs: make(map[string]*ClientSubscription), - rootCtx: rootCtx, - cancelRoot: cancelRoot, - allowSubscribe: true, - serverSubs: make(map[ID]*Subscription), - } - h.unsubscribeCb = newCallback(reflect.Value{}, reflect.ValueOf(h.unsubscribe)) - return h -} - -// handleBatch executes all messages in a batch and returns the responses. -func (h *handler) handleBatch(msgs []*jsonrpcMessage) { - // Emit error response for empty batches: - if len(msgs) == 0 { - h.startCallProc(func(cp *callProc) { - err := h.conn.Write(cp.ctx, errorMessage(&invalidRequestError{"empty batch"})) - if err != nil { - log.Fatal(err) - } - }) - return - } - - // Handle non-call messages first: - calls := make([]*jsonrpcMessage, 0, len(msgs)) - for _, msg := range msgs { - if handled := h.handleImmediate(msg); !handled { - calls = append(calls, msg) - } - } - if len(calls) == 0 { - return - } - // Process calls on a goroutine because they may block indefinitely: - h.startCallProc(func(cp *callProc) { - answers := make([]*jsonrpcMessage, 0, len(msgs)) - for _, msg := range calls { - if answer := h.handleCallMsg(cp, msg); answer != nil { - answers = append(answers, answer) - } - } - h.addSubscriptions(cp.notifiers) - if len(answers) > 0 { - err := h.conn.Write(cp.ctx, answers) - if err != nil { - log.Fatal(err) - } - } - for _, n := range cp.notifiers { - err := n.activate() - if err != nil { - log.Fatal(err) - } - } - }) -} - -// handleMsg handles a single message. -func (h *handler) handleMsg(msg *jsonrpcMessage) { - if ok := h.handleImmediate(msg); ok { - return - } - h.startCallProc(func(cp *callProc) { - answer := h.handleCallMsg(cp, msg) - h.addSubscriptions(cp.notifiers) - if answer != nil { - err := h.conn.Write(cp.ctx, answer) - if err != nil { - log.Fatal(err) - } - } - for _, n := range cp.notifiers { - err := n.activate() - if err != nil { - log.Fatal(err) - } - } - }) -} - -// close cancels all requests except for inflightReq and waits for -// call goroutines to shut down. -func (h *handler) close(err error, inflightReq *requestOp) { - h.cancelAllRequests(err, inflightReq) - h.callWG.Wait() - h.cancelRoot() - h.cancelServerSubscriptions(err) -} - -// addRequestOp registers a request operation. -func (h *handler) addRequestOp(op *requestOp) { - for _, id := range op.ids { - h.respWait[string(id)] = op - } -} - -// removeRequestOps stops waiting for the given request IDs. -func (h *handler) removeRequestOp(op *requestOp) { - for _, id := range op.ids { - delete(h.respWait, string(id)) - } -} - -// cancelAllRequests unblocks and removes pending requests and active subscriptions. -func (h *handler) cancelAllRequests(err error, inflightReq *requestOp) { - didClose := make(map[*requestOp]bool) - if inflightReq != nil { - didClose[inflightReq] = true - } - - for id, op := range h.respWait { - // Remove the op so that later calls will not close op.resp again. - delete(h.respWait, id) - - if !didClose[op] { - op.err = err - close(op.resp) - didClose[op] = true - } - } - for id, sub := range h.clientSubs { - delete(h.clientSubs, id) - sub.quitWithError(err, false) - } -} - -func (h *handler) addSubscriptions(nn []*Notifier) { - h.subLock.Lock() - defer h.subLock.Unlock() - - for _, n := range nn { - if sub := n.takeSubscription(); sub != nil { - h.serverSubs[sub.ID] = sub - } - } -} - -// cancelServerSubscriptions removes all subscriptions and closes their error channels. -func (h *handler) cancelServerSubscriptions(err error) { - h.subLock.Lock() - defer h.subLock.Unlock() - - for id, s := range h.serverSubs { - s.err <- err - close(s.err) - delete(h.serverSubs, id) - } -} - -// startCallProc runs fn in a new goroutine and starts tracking it in the h.calls wait group. -func (h *handler) startCallProc(fn func(*callProc)) { - h.callWG.Add(1) - go func() { - ctx, cancel := context.WithCancel(h.rootCtx) - defer h.callWG.Done() - defer cancel() - fn(&callProc{ctx: ctx}) - }() -} - -// handleImmediate executes non-call messages. It returns false if the message is a -// call or requires a reply. -func (h *handler) handleImmediate(msg *jsonrpcMessage) bool { - // start := time.Now() - switch { - case msg.isNotification(): - if strings.HasSuffix(msg.Method, notificationMethodSuffix) { - h.handleSubscriptionResult(msg) - return true - } - return false - case msg.isResponse(): - h.handleResponse(msg) - // h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "t", time.Since(start)) - return true - default: - return false - } -} - -// handleSubscriptionResult processes subscription notifications. -func (h *handler) handleSubscriptionResult(msg *jsonrpcMessage) { - var result subscriptionResult - if err := json.Unmarshal(msg.Params, &result); err != nil { - // h.log.Debug("Dropping invalid subscription message") - return - } - if h.clientSubs[result.ID] != nil { - h.clientSubs[result.ID].deliver(result.Result) - } -} - -// handleResponse processes method call responses. -func (h *handler) handleResponse(msg *jsonrpcMessage) { - op := h.respWait[string(msg.ID)] - if op == nil { - // h.log.Debug("Unsolicited RPC response", "reqid", idForLog{msg.ID}) - return - } - delete(h.respWait, string(msg.ID)) - // For normal responses, just forward the reply to Call/BatchCall. - if op.sub == nil { - op.resp <- msg - return - } - // For subscription responses, start the subscription if the server - // indicates success. EthSubscribe gets unblocked in either case through - // the op.resp channel. - defer close(op.resp) - if msg.Error != nil { - op.err = msg.Error - return - } - if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil { - go op.sub.start() - h.clientSubs[op.sub.subid] = op.sub - } -} - -// handleCallMsg executes a call message and returns the answer. -func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMessage { - // start := time.Now() - switch { - case msg.isNotification(): - h.handleCall(ctx, msg) - // h.log.Debug("Served "+msg.Method, "t", time.Since(start)) - return nil - case msg.isCall(): - resp := h.handleCall(ctx, msg) - if resp.Error != nil { - // h.log.Warn("Served "+msg.Method, "reqid", idForLog{msg.ID}, "t", time.Since(start), "err", resp.Error.Message) - } else { - // h.log.Debug("Served "+msg.Method, "reqid", idForLog{msg.ID}, "t", time.Since(start)) - } - return resp - case msg.hasValidID(): - return msg.errorResponse(&invalidRequestError{"invalid request"}) - default: - return errorMessage(&invalidRequestError{"invalid request"}) - } -} - -// handleCall processes method calls. -func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage { - if msg.isSubscribe() { - return h.handleSubscribe(cp, msg) - } - var callb *callback - if msg.isUnsubscribe() { - callb = h.unsubscribeCb - } else { - callb = h.reg.callback(msg.Method) - } - if callb == nil { - return msg.errorResponse(&methodNotFoundError{method: msg.Method}) - } - args, err := parsePositionalArguments(msg.Params, callb.argTypes) - if err != nil { - return msg.errorResponse(&invalidParamsError{err.Error()}) - } - - return h.runMethod(cp.ctx, msg, callb, args) -} - -// handleSubscribe processes *_subscribe method calls. -func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage { - if !h.allowSubscribe { - return msg.errorResponse(ErrNotificationsUnsupported) - } - - // Subscription method name is first argument. - name, err := parseSubscriptionName(msg.Params) - if err != nil { - return msg.errorResponse(&invalidParamsError{err.Error()}) - } - namespace := msg.namespace() - callb := h.reg.subscription(namespace, name) - if callb == nil { - return msg.errorResponse(&subscriptionNotFoundError{namespace, name}) - } - - // Parse subscription name arg too, but remove it before calling the callback. - argTypes := append([]reflect.Type{stringType}, callb.argTypes...) - args, err := parsePositionalArguments(msg.Params, argTypes) - if err != nil { - return msg.errorResponse(&invalidParamsError{err.Error()}) - } - args = args[1:] - - // Install notifier in context so the subscription handler can find it. - n := &Notifier{h: h, namespace: namespace} - cp.notifiers = append(cp.notifiers, n) - ctx := context.WithValue(cp.ctx, notifierKey{}, n) - - return h.runMethod(ctx, msg, callb, args) -} - -// runMethod runs the Go callback for an RPC method. -func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *callback, args []reflect.Value) *jsonrpcMessage { - result, err := callb.call(ctx, msg.Method, args) - if err != nil { - return msg.errorResponse(err) - } - return msg.response(result) -} - -// unsubscribe is the callback function for all *_unsubscribe calls. -func (h *handler) unsubscribe(ctx context.Context, id ID) (bool, error) { - h.subLock.Lock() - defer h.subLock.Unlock() - - s := h.serverSubs[id] - if s == nil { - return false, ErrSubscriptionNotFound - } - close(s.err) - delete(h.serverSubs, id) - return true, nil -} - -type idForLog struct{ json.RawMessage } - -func (id idForLog) String() string { - if s, err := strconv.Unquote(string(id.RawMessage)); err == nil { - return s - } - return string(id.RawMessage) -} diff --git a/conn/json.go b/conn/json.go index 71fe4365..1dc0b628 100644 --- a/conn/json.go +++ b/conn/json.go @@ -23,14 +23,16 @@ import ( "errors" "fmt" "io" - "log" "reflect" "strings" "sync" "time" + + "github.com/sirupsen/logrus" ) const ( + defaultErrorCode = -32000 vsn = "2.0" serviceMethodSeparator = "_" subscribeMethodSuffix = "_subscribe" @@ -218,7 +220,7 @@ func (c *jsonCodec) Write(ctx context.Context, v interface{}) error { } err := c.conn.SetWriteDeadline(deadline) if err != nil { - log.Print(err) + logrus.Print(err) } return c.encode(v) } @@ -229,7 +231,7 @@ func (c *jsonCodec) Close() { close(c.closed) err := c.conn.Close() if err != nil { - log.Fatal(err) + logrus.Fatal(err) } }) } @@ -248,21 +250,21 @@ func parseMessage(raw json.RawMessage) ([]*jsonrpcMessage, bool) { msgs := []*jsonrpcMessage{{}} err := json.Unmarshal(raw, &msgs[0]) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } return msgs, false } dec := json.NewDecoder(bytes.NewReader(raw)) _, err := dec.Token() // skip '[' if err != nil { - log.Fatal(err) + logrus.Fatal(err) } var msgs []*jsonrpcMessage for dec.More() { msgs = append(msgs, new(jsonrpcMessage)) err := dec.Decode(&msgs[len(msgs)-1]) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } } return msgs, true diff --git a/conn/service.go b/conn/service.go deleted file mode 100644 index 514ed5ca..00000000 --- a/conn/service.go +++ /dev/null @@ -1,283 +0,0 @@ -// 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 conn - -import ( - "context" - "errors" - "fmt" - "reflect" - "runtime" - "strings" - "sync" - "unicode" - "unicode/utf8" -) - -var ( - contextType = reflect.TypeOf((*context.Context)(nil)).Elem() - errorType = reflect.TypeOf((*error)(nil)).Elem() - subscriptionType = reflect.TypeOf(Subscription{}) - stringType = reflect.TypeOf("") -) - -type serviceRegistry struct { - mu sync.Mutex - services map[string]service -} - -// service represents a registered object. -type service struct { - name string // name for service - callbacks map[string]*callback // registered handlers - subscriptions map[string]*callback // available subscriptions/notifications -} - -// callback is a method callback which was registered in the server -type callback struct { - fn reflect.Value // the function - rcvr reflect.Value // receiver object of method, set if fn is method - argTypes []reflect.Type // input argument types - hasCtx bool // method's first argument is a context (not included in argTypes) - errPos int // err return idx, of -1 when method cannot return error - isSubscribe bool // true if this is a subscription callback -} - -func (r *serviceRegistry) registerName(name string, rcvr interface{}) error { - rcvrVal := reflect.ValueOf(rcvr) - if name == "" { - return fmt.Errorf("no service name for type %s", rcvrVal.Type().String()) - } - callbacks := suitableCallbacks(rcvrVal) - if len(callbacks) == 0 { - return fmt.Errorf("service %T doesn't have any suitable methods/subscriptions to expose", rcvr) - } - - r.mu.Lock() - defer r.mu.Unlock() - if r.services == nil { - r.services = make(map[string]service) - } - svc, ok := r.services[name] - if !ok { - svc = service{ - name: name, - callbacks: make(map[string]*callback), - subscriptions: make(map[string]*callback), - } - r.services[name] = svc - } - for name, cb := range callbacks { - if cb.isSubscribe { - svc.subscriptions[name] = cb - } else { - svc.callbacks[name] = cb - } - } - return nil -} - -// callback returns the callback corresponding to the given RPC method name. -func (r *serviceRegistry) callback(method string) *callback { - elem := strings.SplitN(method, serviceMethodSeparator, 2) - if len(elem) != 2 { - return nil - } - r.mu.Lock() - defer r.mu.Unlock() - return r.services[elem[0]].callbacks[elem[1]] -} - -// subscription returns a subscription callback in the given service. -func (r *serviceRegistry) subscription(service, name string) *callback { - r.mu.Lock() - defer r.mu.Unlock() - return r.services[service].subscriptions[name] -} - -// suitableCallbacks iterates over the methods of the given type. It determines if a method -// satisfies the criteria for a RPC callback or a subscription callback and adds it to the -// collection of callbacks. See server documentation for a summary of these criteria. -func suitableCallbacks(receiver reflect.Value) map[string]*callback { - typ := receiver.Type() - callbacks := make(map[string]*callback) - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - if method.PkgPath != "" { - continue // method not exported - } - cb := newCallback(receiver, method.Func) - if cb == nil { - continue // function invalid - } - name := formatName(method.Name) - callbacks[name] = cb - } - return callbacks -} - -// newCallback turns fn (a function) into a callback object. It returns nil if the function -// is unsuitable as an RPC callback. -func newCallback(receiver, fn reflect.Value) *callback { - fntype := fn.Type() - c := &callback{fn: fn, rcvr: receiver, errPos: -1, isSubscribe: isPubSub(fntype)} - // Determine parameter types. They must all be exported or builtin types. - c.makeArgTypes() - if !allExportedOrBuiltin(c.argTypes) { - return nil - } - // Verify return types. The function must return at most one error - // and/or one other non-error value. - outs := make([]reflect.Type, fntype.NumOut()) - for i := 0; i < fntype.NumOut(); i++ { - outs[i] = fntype.Out(i) - } - if len(outs) > 2 || !allExportedOrBuiltin(outs) { - return nil - } - // If an error is returned, it must be the last returned value. - switch { - case len(outs) == 1 && isErrorType(outs[0]): - c.errPos = 0 - case len(outs) == 2: - if isErrorType(outs[0]) || !isErrorType(outs[1]) { - return nil - } - c.errPos = 1 - } - return c -} - -// makeArgTypes composes the argTypes list. -func (c *callback) makeArgTypes() { - fntype := c.fn.Type() - // Skip receiver and context.Context parameter (if present). - firstArg := 0 - if c.rcvr.IsValid() { - firstArg++ - } - if fntype.NumIn() > firstArg && fntype.In(firstArg) == contextType { - c.hasCtx = true - firstArg++ - } - // Add all remaining parameters. - c.argTypes = make([]reflect.Type, fntype.NumIn()-firstArg) - for i := firstArg; i < fntype.NumIn(); i++ { - c.argTypes[i-firstArg] = fntype.In(i) - } -} - -// call invokes the callback. -func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res interface{}, errRes error) { - // Create the argument slice. - fullargs := make([]reflect.Value, 0, 2+len(args)) - if c.rcvr.IsValid() { - fullargs = append(fullargs, c.rcvr) - } - if c.hasCtx { - fullargs = append(fullargs, reflect.ValueOf(ctx)) - } - fullargs = append(fullargs, args...) - - // Catch panic while running the callback. - defer func() { - if err := recover(); err != nil { - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - // log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf)) - errRes = errors.New("method handler crashed") - } - }() - // Run the callback. - results := c.fn.Call(fullargs) - if len(results) == 0 { - return nil, nil - } - if c.errPos >= 0 && !results[c.errPos].IsNil() { - // Method has returned non-nil error value. - err := results[c.errPos].Interface().(error) - return reflect.Value{}, err - } - return results[0].Interface(), nil -} - -// Is this an exported - upper case - name? -func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) -} - -// Are all those types exported or built-in? -func allExportedOrBuiltin(types []reflect.Type) bool { - for _, typ := range types { - for typ.Kind() == reflect.Ptr { - typ = typ.Elem() - } - // PkgPath will be non-empty even for an exported type, - // so we need to check the type name as well. - if !isExported(typ.Name()) && typ.PkgPath() != "" { - return false - } - } - return true -} - -// Is t context.Context or *context.Context? -func isContextType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - return t == contextType -} - -// Does t satisfy the error interface? -func isErrorType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - return t.Implements(errorType) -} - -// Is t Subscription or *Subscription? -func isSubscriptionType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - return t == subscriptionType -} - -// isPubSub tests whether the given method has as as first argument a context.Context and -// returns the pair (Subscription, error). -func isPubSub(methodType reflect.Type) bool { - // numIn(0) is the receiver type - if methodType.NumIn() < 2 || methodType.NumOut() != 2 { - return false - } - return isContextType(methodType.In(1)) && - isSubscriptionType(methodType.Out(0)) && - isErrorType(methodType.Out(1)) -} - -// formatName converts to first character of name to lowercase. -func formatName(name string) string { - ret := []rune(name) - if len(ret) > 0 { - ret[0] = unicode.ToLower(ret[0]) - } - return string(ret) -} diff --git a/conn/subscription.go b/conn/subscription.go deleted file mode 100644 index 41e99158..00000000 --- a/conn/subscription.go +++ /dev/null @@ -1,334 +0,0 @@ -// 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 conn - -import ( - "bufio" - "container/list" - "context" - crand "crypto/rand" - "encoding/binary" - "encoding/hex" - "encoding/json" - "errors" - "log" - "math/rand" - "reflect" - "strings" - "sync" - "time" -) - -var ( - // ErrNotificationsUnsupported is returned when the connection doesn't support notifications - ErrNotificationsUnsupported = errors.New("notifications not supported") - // ErrSubscriptionNotFound is returned when the notification for the given id is not found - ErrSubscriptionNotFound = errors.New("subscription not found") -) - -var globalGen = randomIDGenerator() - -// ID defines a pseudo random number that is used to identify RPC subscriptions. -type ID string - -// NewID returns a new, random ID. -func NewID() ID { - return globalGen() -} - -// randomIDGenerator returns a function generates a random IDs. -func randomIDGenerator() func() ID { - seed, err := binary.ReadVarint(bufio.NewReader(crand.Reader)) - if err != nil { - seed = int64(time.Now().Nanosecond()) - } - var ( - mu sync.Mutex - rng = rand.New(rand.NewSource(seed)) - ) - return func() ID { - mu.Lock() - defer mu.Unlock() - id := make([]byte, 16) - _, err = rng.Read(id) - if err != nil { - log.Fatal(err) - } - return encodeID(id) - } -} - -func encodeID(b []byte) ID { - id := hex.EncodeToString(b) - id = strings.TrimLeft(id, "0") - if id == "" { - id = "0" // ID's are RPC quantities, no leading zero's and 0 is 0x0. - } - return ID("0x" + id) -} - -type notifierKey struct{} - -// NotifierFromContext returns the Notifier value stored in ctx, if any. -func NotifierFromContext(ctx context.Context) (*Notifier, bool) { - n, ok := ctx.Value(notifierKey{}).(*Notifier) - return n, ok -} - -// Notifier is tied to a RPC connection that supports subscriptions. -// Server callbacks use the notifier to send notifications. -type Notifier struct { - h *handler - namespace string - - mu sync.Mutex - sub *Subscription - buffer []json.RawMessage - callReturned bool - activated bool -} - -// CreateSubscription returns a new subscription that is coupled to the -// RPC connection. By default subscriptions are inactive and notifications -// are dropped until the subscription is marked as active. This is done -// by the RPC server after the subscription ID is send to the client. -func (n *Notifier) CreateSubscription() *Subscription { - n.mu.Lock() - defer n.mu.Unlock() - - if n.sub != nil { - panic("can't create multiple subscriptions with Notifier") - } else if n.callReturned { - panic("can't create subscription after subscribe call has returned") - } - n.sub = &Subscription{ID: n.h.idgen(), namespace: n.namespace, err: make(chan error, 1)} - return n.sub -} - -// Notify sends a notification to the client with the given data as payload. -// If an error occurs the RPC connection is closed and the error is returned. -func (n *Notifier) Notify(id ID, data interface{}) error { - enc, err := json.Marshal(data) - if err != nil { - return err - } - - n.mu.Lock() - defer n.mu.Unlock() - - if n.sub == nil { - panic("can't Notify before subscription is created") - } else if n.sub.ID != id { - panic("Notify with wrong ID") - } - if n.activated { - return n.send(n.sub, enc) - } - n.buffer = append(n.buffer, enc) - return nil -} - -// Closed returns a channel that is closed when the RPC connection is closed. -// Deprecated: use subscription error channel -func (n *Notifier) Closed() <-chan interface{} { - return n.h.conn.Closed() -} - -// takeSubscription returns the subscription (if one has been created). No subscription can -// be created after this call. -func (n *Notifier) takeSubscription() *Subscription { - n.mu.Lock() - defer n.mu.Unlock() - n.callReturned = true - return n.sub -} - -// acticate is called after the subscription ID was sent to client. Notifications are -// buffered before activation. This prevents notifications being sent to the client before -// the subscription ID is sent to the client. -func (n *Notifier) activate() error { - n.mu.Lock() - defer n.mu.Unlock() - - for _, data := range n.buffer { - if err := n.send(n.sub, data); err != nil { - return err - } - } - n.activated = true - return nil -} - -func (n *Notifier) send(sub *Subscription, data json.RawMessage) error { - params, _ := json.Marshal(&subscriptionResult{ID: string(sub.ID), Result: data}) - ctx := context.Background() - return n.h.conn.Write(ctx, &jsonrpcMessage{ - Version: vsn, - Method: n.namespace + notificationMethodSuffix, - Params: params, - }) -} - -// A Subscription is created by a notifier and tight to that notifier. The client can use -// this subscription to wait for an unsubscribe request for the client, see Err(). -type Subscription struct { - ID ID - namespace string - err chan error // closed on unsubscribe -} - -// Err returns a channel that is closed when the client send an unsubscribe request. -func (s *Subscription) Err() <-chan error { - return s.err -} - -// MarshalJSON marshals a subscription as its ID. -func (s *Subscription) MarshalJSON() ([]byte, error) { - return json.Marshal(s.ID) -} - -// ClientSubscription is a subscription established through the Connection's Subscribe or -// EthSubscribe methods. -type ClientSubscription struct { - client *Connection - etype reflect.Type - channel reflect.Value - namespace string - subid string - in chan json.RawMessage - - quitOnce sync.Once // ensures quit is closed once - quit chan struct{} // quit is closed when the subscription exits - errOnce sync.Once // ensures err is closed once - err chan error -} - -func newClientSubscription(c *Connection, namespace string, channel reflect.Value) *ClientSubscription { - sub := &ClientSubscription{ - client: c, - namespace: namespace, - etype: channel.Type().Elem(), - channel: channel, - quit: make(chan struct{}), - err: make(chan error, 1), - in: make(chan json.RawMessage), - } - return sub -} - -// Err returns the subscription error channel. The intended use of Err is to schedule -// resubscription when the client connection is closed unexpectedly. -// -// The error channel receives a value when the subscription has ended due -// to an error. The received error is nil if Close has been called -// on the underlying client and no other error has occurred. -// -// The error channel is closed when Unsubscribe is called on the subscription. -func (sub *ClientSubscription) Err() <-chan error { - return sub.err -} - -// Unsubscribe unsubscribes the notification and closes the error channel. -// It can safely be called more than once. -func (sub *ClientSubscription) Unsubscribe() { - sub.quitWithError(nil, true) - sub.errOnce.Do(func() { close(sub.err) }) -} - -func (sub *ClientSubscription) quitWithError(err error, unsubscribeServer bool) { - sub.quitOnce.Do(func() { - // The dispatch loop won't be able to execute the unsubscribe call - // if it is blocked on deliver. Close sub.quit first because it - // unblocks deliver. - close(sub.quit) - if unsubscribeServer { - err := sub.requestUnsubscribe() - if err != nil { - log.Fatal(err) - } - } - if err != nil { - if err == ErrClientQuit { - err = nil // Adhere to subscription semantics. - } - sub.err <- err - } - }) -} - -func (sub *ClientSubscription) deliver(result json.RawMessage) (ok bool) { - select { - case sub.in <- result: - return true - case <-sub.quit: - return false - } -} - -func (sub *ClientSubscription) start() { - sub.quitWithError(sub.forward()) -} - -func (sub *ClientSubscription) forward() (err error, unsubscribeServer bool) { - cases := []reflect.SelectCase{ - {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.quit)}, - {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.in)}, - {Dir: reflect.SelectSend, Chan: sub.channel}, - } - buffer := list.New() - defer buffer.Init() - for { - var chosen int - var recv reflect.Value - if buffer.Len() == 0 { - // Idle, omit send case. - chosen, recv, _ = reflect.Select(cases[:2]) - } else { - // Non-empty buffer, send the first queued item. - cases[2].Send = reflect.ValueOf(buffer.Front().Value) - chosen, recv, _ = reflect.Select(cases) - } - - switch chosen { - case 0: // <-sub.quit - return nil, false - case 1: // <-sub.in - val, err := sub.unmarshal(recv.Interface().(json.RawMessage)) - if err != nil { - return err, true - } - if buffer.Len() == maxClientSubscriptionBuffer { - return ErrSubscriptionQueueOverflow, true - } - buffer.PushBack(val) - case 2: // sub.channel<- - cases[2].Send = reflect.Value{} // Don't hold onto the value. - buffer.Remove(buffer.Front()) - } - } -} - -func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, error) { - val := reflect.New(sub.etype) - err := json.Unmarshal(result, val.Interface()) - return val.Elem().Interface(), err -} - -func (sub *ClientSubscription) requestUnsubscribe() error { - var result interface{} - return sub.client.Call(&result, sub.namespace+unsubscribeMethodSuffix, sub.subid) -} diff --git a/core/types/block.go b/core/types/block.go new file mode 100644 index 00000000..88722ba9 --- /dev/null +++ b/core/types/block.go @@ -0,0 +1,105 @@ +package types + +type Block struct { + DbHash string `json:"dbHash"` + ExtraData []string `json:"extraData"` + GasLimit string `json:"gasLimit"` + GasUsed string `json:"gasUsed"` + Hash string `json:"hash"` + LogsBloom string `json:"logsBloom"` + Number string `json:"number"` + ParentHash string `json:"parentHash"` + ReceiptsRoot string `json:"receiptsRoot"` + Sealer string `json:"sealer"` + SealerList []string `json:"sealerList"` + SignatureList []Signature `json:"signatureList"` + StateRoot string `json:"stateRoot"` + Timestamp string `json:"timestamp"` + Transactions []interface{} `json:"transactions"` + TransactionsRoot string `json:"transactionsRoot"` +} + +type Signature struct { + Index string `json:"index"` + Signature string `json:"signature"` +} + +// GetIndex returns the signature index string +func (s *Signature) GetIndex() string { + return s.Index +} + +// GetSignature returns signature string +func (s *Signature) GetSignature() string { + return s.Signature +} + +// GetDbHash returns records changes to transaction data hash string +func (B *Block) GetDbHash() string { + return B.DbHash +} + +// GetGasLimit returns the block max gas limit string +func (B *Block) GetGasLimit() string { + return B.GasLimit +} + +// GetGasUsed returns the block gas used string +func (B *Block) GetGasUsed() string { + return B.GasUsed +} + +// GetHash returns the block hash string +func (B *Block) GetHash() string { + return B.Hash +} + +// GetLogsBloom returns the block logs bloom string +func (B *Block) GetLogsBloom() string { + return B.LogsBloom +} + +// GetNumber returns the block number string +func (B *Block) GetNumber() string { + return B.Number +} + +// GetParentHash returns parent block hash string +func (B *Block) GetParentHash() string { + return B.ParentHash +} + +// GetReceiptsRoot returns the block receipts root string +func (B *Block) GetReceiptsRoot() string { + return B.ReceiptsRoot +} + +// GetSealer returns the sealer node sequence number string +func (B *Block) GetSealer() string { + return B.Sealer +} + +// GetSealerList returns the sealer node list +func (B *Block) GetSealerList() []string { + return B.SealerList +} + +// GetSignatureList returns the block signature list +func (B *Block) GetSignatureList() []Signature { + return B.SignatureList +} + +// GetTimestamp returns the block timestamp string +func (B *Block) GetTimestamp() string { + return B.Timestamp +} + +// GetTransactions returns the blcok transcation list +func (B *Block) GetTransactions() []interface{} { + return B.Transactions +} + +// GetTransactionsRoot returns the block all transcation root string +func (B *Block) GetTransactionsRoot() string { + return B.TransactionsRoot +} diff --git a/core/types/client_version.go b/core/types/client_version.go new file mode 100644 index 00000000..26e861f1 --- /dev/null +++ b/core/types/client_version.go @@ -0,0 +1,46 @@ +package types + +type ClientVersion struct { + BuildTime string `json:"Build Time"` + BuildType string `json:"Build Type"` + ChainId string `json:"Chain Id"` + FiscoBcosVersion string `json:"FISCO-BCOS Version"` + GitBranch string `json:"Git Branch"` + GitCommitHash string `json:"Git Commit Hash"` + SupportedVersion string `json:"Supported Version"` +} + +// GetBuildTime returns the client build time string +func (c *ClientVersion) GetBuildTime() string { + return c.BuildTime +} + +// GetBuildType returns Compile machine environment string +func (c *ClientVersion) GetBuildType() string { + return c.BuildType +} + +// GetChainId returns chain id string +func (c *ClientVersion) GetChainId() string { + return c.ChainId +} + +// GetFISCOBCOSVersion returns the node version string +func (c *ClientVersion) GetFiscoBcosVersion() string { + return c.FiscoBcosVersion +} + +// GetGitBranch returns versions branch string +func (c *ClientVersion) GetGitBranch() string { + return c.GitBranch +} + +// GetGitCommitHash returns git commit hash string +func (c *ClientVersion) GetGitCommitHash() string { + return c.GitCommitHash +} + +// GetSupportedVersion returns the node supported version string +func (c *ClientVersion) GetSupportedVersion() string { + return c.SupportedVersion +} diff --git a/core/types/log.go b/core/types/log.go index 4655ae50..8abec336 100644 --- a/core/types/log.go +++ b/core/types/log.go @@ -55,6 +55,15 @@ type Log struct { Removed bool `json:"removed"` } +type EventLogParams struct { + FromBlock string `json:"fromBlock"` + ToBlock string `json:"toBlock"` + Addresses []string `json:"addresses"` + Topics []string `json:"topics"` + GroupID string `json:"groupID"` + FilterID string `json:"filterID"` +} + type logMarshaling struct { Data hexutil.Bytes BlockNumber hexutil.Uint64 @@ -150,5 +159,5 @@ type NewLog struct { // supplied by the contract, usually ABI-encoded Data string `json:"data"` // list of topics provided by the contract. - Topics []interface{} `json:"topics" ` + Topics []string `json:"topics"` } diff --git a/core/types/node.go b/core/types/node.go new file mode 100644 index 00000000..a81dba69 --- /dev/null +++ b/core/types/node.go @@ -0,0 +1,34 @@ +package types + +type Node struct { + Agency string `json:"Agency"` + IPAndPort string `json:"IPAndPort"` + Node string `json:"Node"` + NodeId string `json:"NodeID"` + Topic []string `json:"Topic"` +} + +// GetAgency returns the node agency string +func (n *Node) GetAgency() string { + return n.Agency +} + +// GetIPAndPort returns the node ip and port string +func (n *Node) GetIPAndPort() string { + return n.IPAndPort +} + +// GetNode returns the node string +func (n *Node) GetNode() string { + return n.Node +} + +// GetNodeId returns the node id string +func (n *Node) GetNodeId() string { + return n.NodeId +} + +// GetTopic returns the node attention topic information string +func (n *Node) GetTopic() []string { + return n.Topic +} diff --git a/core/types/sync_status.go b/core/types/sync_status.go new file mode 100644 index 00000000..5936f200 --- /dev/null +++ b/core/types/sync_status.go @@ -0,0 +1,91 @@ +package types + +type SyncStatus struct { + BlockNumber int `json:"blockNumber"` + GenesisHash string `json:"genesisHash"` + IsSyncing bool `json:"isSyncing"` + KnownHighestNumber int `json:"knownHighestNumber"` + KnownLatestHash string `json:"knownLatestHash"` + LatestHash string `json:"latestHash"` + NodeId string `json:"nodeId"` + Peers []Peer `json:"peers"` + ProtocolId int `json:"protocolId"` + TxPoolSize string `json:"txPoolSize"` +} + +type Peer struct { + BlockNumber int `json:"blockNumber"` + GenesisHash string `json:"genesisHash"` + LatestHash string `json:"latestHash"` + NodeId string `json:"nodeId"` +} + +// GetBlockNumber returns the laster block number int +func (p *Peer) GetBlockNumber() int { + return p.BlockNumber +} + +// GetGenesisHash returns the original block hash string +func (p *Peer) GetGenesisHash() string { + return p.GenesisHash +} + +// GetLatestHash returns the laster block hash string +func (p *Peer) GetLatestHash() string { + return p.LatestHash +} + +// GetNodeId returns the node id string +func (p *Peer) GetNodeId() string { + return p.NodeId +} + +// GetBlockNumber returns the block number int +func (s *SyncStatus) GetBlockNumber() int { + return s.BlockNumber +} + +// GetGenesisHash returns the original block hash string +func (s *SyncStatus) GetGenesisHash() string { + return s.GenesisHash +} + +// GetIsSyncing returns sync status +func (s *SyncStatus) GetIsSyncing() bool { + return s.IsSyncing +} + +// GetKnownHighestNumber returns the known highest number int +func (s *SyncStatus) GetKnownHighestNumber() int { + return s.KnownHighestNumber +} + +// GetKnownLatestHash returns the known latest Hash string +func (s *SyncStatus) GetKnownLatestHash() string { + return s.KnownLatestHash +} + +// GetLatestHash returns the last hash string +func (s *SyncStatus) GetLatestHash() string { + return s.LatestHash +} + +// GetNodeId returns the node id string +func (s *SyncStatus) GetNodeId() string { + return s.NodeId +} + +// GetPeers returns the peers +func (s *SyncStatus) GetPeers() []Peer { + return s.Peers +} + +// GetProtocolId returns the transaction protocol id int +func (s *SyncStatus) GetProtocolId() int { + return s.ProtocolId +} + +// GetTxPoolSize returns the transaction pool size +func (s *SyncStatus) GetTxPoolSize() string { + return s.TxPoolSize +} diff --git a/core/types/transaction.go b/core/types/transaction.go index 33481cf2..2ddaea0e 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "container/heap" "errors" "fmt" "io" @@ -402,68 +401,6 @@ func (s *TxByPrice) Pop() interface{} { return x } -// TransactionsByPriceAndNonce represents a set of transactions that can return -// transactions in a profit-maximizing sorted order, while supporting removing -// entire batches of transactions for non-executable accounts. -type TransactionsByPriceAndNonce struct { - txs map[common.Address]Transactions // Per account nonce-sorted list of transactions - heads TxByPrice // Next transaction for each unique account (price heap) - signer Signer // Signer for the set of transactions -} - -// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve -// price sorted transactions in a nonce-honouring way. -// -// Note, the input map is reowned so the caller should not interact any more with -// if after providing it to the constructor. -func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { - // Initialize a price based heap with the head transactions - heads := make(TxByPrice, 0, len(txs)) - for from, accTxs := range txs { - heads = append(heads, accTxs[0]) - // Ensure the sender address is from the signer - acc, _ := Sender(signer, accTxs[0]) - txs[acc] = accTxs[1:] - if from != acc { - delete(txs, from) - } - } - heap.Init(&heads) - - // Assemble and return the transaction set - return &TransactionsByPriceAndNonce{ - txs: txs, - heads: heads, - signer: signer, - } -} - -// Peek returns the next transaction by price. -func (t *TransactionsByPriceAndNonce) Peek() *Transaction { - if len(t.heads) == 0 { - return nil - } - return t.heads[0] -} - -// Shift replaces the current best head with the next one from the same account. -func (t *TransactionsByPriceAndNonce) Shift() { - acc, _ := Sender(t.signer, t.heads[0]) - if txs, ok := t.txs[acc]; ok && len(txs) > 0 { - t.heads[0], t.txs[acc] = txs[0], txs[1:] - heap.Fix(&t.heads, 0) - } else { - heap.Pop(&t.heads) - } -} - -// Pop removes the best transaction, *not* replacing it with the next one from -// the same account. This should be used when a transaction cannot be executed -// and hence all subsequent ones should be discarded from the same account. -func (t *TransactionsByPriceAndNonce) Pop() { - heap.Pop(&t.heads) -} - // Message is a fully derived transaction and implements core.Message // // NOTE: In a future PR this will be removed. diff --git a/core/types/transaction_count.go b/core/types/transaction_count.go new file mode 100644 index 00000000..97fb4903 --- /dev/null +++ b/core/types/transaction_count.go @@ -0,0 +1,22 @@ +package types + +type TransactionCount struct { + BlockNumber string `json:"blockNumber"` + FailedTxSum string `json:"failedTxSum"` + TxSum string `json:"txSum"` +} + +// GetBlockNumber returns the transaction block height string +func (t *TransactionCount) GetBlockNumber() string { + return t.BlockNumber +} + +// GetFailedTxSum returns the transaction failed sum string +func (t *TransactionCount) GetFailedTxSum() string { + return t.FailedTxSum +} + +// GetTxSum returns the transaction sum string +func (t *TransactionCount) GetTxSum() string { + return t.TxSum +} diff --git a/core/types/transaction_detail.go b/core/types/transaction_detail.go new file mode 100644 index 00000000..f8636b48 --- /dev/null +++ b/core/types/transaction_detail.go @@ -0,0 +1,70 @@ +package types + +type TransactionDetail struct { + BlockHash string `json:"blockHash"` + BlockNumber string `json:"blockNumber"` + From string `json:"from"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Hash string `json:"hash"` + Input string `json:"input"` + Nonce string `json:"nonce"` + To string `json:"to"` + TransactionIndex string `json:"transactionIndex"` + Value string `json:"value"` +} + +// GetBlockHash returns the block hash string +func (t *TransactionDetail) GetBlockHash() string { + return t.BlockHash +} + +// GetBlockNumber returns the blcok number string +func (t *TransactionDetail) GetBlockNumber() string { + return t.BlockNumber +} + +// GetValue returns the transaction pfrom address string +func (t *TransactionDetail) GetFrom() string { + return t.From +} + +// GetValue returns the transaction gas string +func (t *TransactionDetail) GetGas() string { + return t.Gas +} + +// GetValue returns the transaction gas price string +func (t *TransactionDetail) GetGasPrice() string { + return t.GasPrice +} + +// GetValue returns the transaction hash string +func (t *TransactionDetail) GetHash() string { + return t.Hash +} + +// GetValue returns the transaction input string +func (t *TransactionDetail) GetInput() string { + return t.Input +} + +// GetValue returns the transaction once string +func (t *TransactionDetail) GetNonce() string { + return t.Nonce +} + +// GetValue returns the transaction to address string +func (t *TransactionDetail) GetTo() string { + return t.To +} + +// GetValue returns the transaction index string +func (t *TransactionDetail) GetTransactionIndex() string { + return t.TransactionIndex +} + +// GetValue returns the transaction value string +func (t *TransactionDetail) GetValue() string { + return t.Value +} diff --git a/core/types/transaction_pending.go b/core/types/transaction_pending.go new file mode 100644 index 00000000..9948d2ce --- /dev/null +++ b/core/types/transaction_pending.go @@ -0,0 +1,52 @@ +package types + +type TransactionPending struct { + From string `json:"from"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Hash string `json:"hash"` + Input string `json:"input"` + Nonce string `json:"nonce"` + To string `json:"to"` + Value string `json:"value"` +} + +// GetFrom returns the transaction from address string +func (t *TransactionPending) GetFrom() string { + return t.From +} + +// GetGas returns the transaction gas string +func (t *TransactionPending) GetGas() string { + return t.Gas +} + +// GetGasPrice returns the transaction gas price string +func (t *TransactionPending) GetGasPrice() string { + return t.GasPrice +} + +// GetHash returns the transaction hash string +func (t *TransactionPending) GetHash() string { + return t.Hash +} + +// GetInput returns the transaction input string +func (t *TransactionPending) GetInput() string { + return t.Input +} + +// GetNonce returns the transaction nonce string +func (t *TransactionPending) GetNonce() string { + return t.Nonce +} + +// GetTo returns the transaction to address string +func (t *TransactionPending) GetTo() string { + return t.To +} + +// GetValue returns the transaction pengding string +func (t *TransactionPending) GetValue() string { + return t.Value +} diff --git a/event/event.go b/event/event.go deleted file mode 100644 index 42327873..00000000 --- a/event/event.go +++ /dev/null @@ -1,217 +0,0 @@ -// 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 event deals with subscriptions to real-time events. -package event - -import ( - "errors" - "fmt" - "reflect" - "sync" - "time" -) - -// TypeMuxEvent is a time-tagged notification pushed to subscribers. -type TypeMuxEvent struct { - Time time.Time - Data interface{} -} - -// A TypeMux dispatches events to registered receivers. Receivers can be -// registered to handle events of certain type. Any operation -// called after mux is stopped will return ErrMuxClosed. -// -// The zero value is ready to use. -// -// Deprecated: use Feed -type TypeMux struct { - mutex sync.RWMutex - subm map[reflect.Type][]*TypeMuxSubscription - stopped bool -} - -// ErrMuxClosed is returned when Posting on a closed TypeMux. -var ErrMuxClosed = errors.New("event: mux closed") - -// Subscribe creates a subscription for events of the given types. The -// subscription's channel is closed when it is unsubscribed -// or the mux is closed. -func (mux *TypeMux) Subscribe(types ...interface{}) *TypeMuxSubscription { - sub := newsub(mux) - mux.mutex.Lock() - defer mux.mutex.Unlock() - if mux.stopped { - // set the status to closed so that calling Unsubscribe after this - // call will short circuit. - sub.closed = true - close(sub.postC) - } else { - if mux.subm == nil { - mux.subm = make(map[reflect.Type][]*TypeMuxSubscription) - } - for _, t := range types { - rtyp := reflect.TypeOf(t) - oldsubs := mux.subm[rtyp] - if find(oldsubs, sub) != -1 { - panic(fmt.Sprintf("event: duplicate type %s in Subscribe", rtyp)) - } - subs := make([]*TypeMuxSubscription, len(oldsubs)+1) - copy(subs, oldsubs) - subs[len(oldsubs)] = sub - mux.subm[rtyp] = subs - } - } - return sub -} - -// Post sends an event to all receivers registered for the given type. -// It returns ErrMuxClosed if the mux has been stopped. -func (mux *TypeMux) Post(ev interface{}) error { - event := &TypeMuxEvent{ - Time: time.Now(), - Data: ev, - } - rtyp := reflect.TypeOf(ev) - mux.mutex.RLock() - if mux.stopped { - mux.mutex.RUnlock() - return ErrMuxClosed - } - subs := mux.subm[rtyp] - mux.mutex.RUnlock() - for _, sub := range subs { - sub.deliver(event) - } - return nil -} - -// Stop closes a mux. The mux can no longer be used. -// Future Post calls will fail with ErrMuxClosed. -// Stop blocks until all current deliveries have finished. -func (mux *TypeMux) Stop() { - mux.mutex.Lock() - for _, subs := range mux.subm { - for _, sub := range subs { - sub.closewait() - } - } - mux.subm = nil - mux.stopped = true - mux.mutex.Unlock() -} - -func (mux *TypeMux) del(s *TypeMuxSubscription) { - mux.mutex.Lock() - for typ, subs := range mux.subm { - if pos := find(subs, s); pos >= 0 { - if len(subs) == 1 { - delete(mux.subm, typ) - } else { - mux.subm[typ] = posdelete(subs, pos) - } - } - } - s.mux.mutex.Unlock() -} - -func find(slice []*TypeMuxSubscription, item *TypeMuxSubscription) int { - for i, v := range slice { - if v == item { - return i - } - } - return -1 -} - -func posdelete(slice []*TypeMuxSubscription, pos int) []*TypeMuxSubscription { - news := make([]*TypeMuxSubscription, len(slice)-1) - copy(news[:pos], slice[:pos]) - copy(news[pos:], slice[pos+1:]) - return news -} - -// TypeMuxSubscription is a subscription established through TypeMux. -type TypeMuxSubscription struct { - mux *TypeMux - created time.Time - closeMu sync.Mutex - closing chan struct{} - closed bool - - // these two are the same channel. they are stored separately so - // postC can be set to nil without affecting the return value of - // Chan. - postMu sync.RWMutex - readC <-chan *TypeMuxEvent - postC chan<- *TypeMuxEvent -} - -func newsub(mux *TypeMux) *TypeMuxSubscription { - c := make(chan *TypeMuxEvent) - return &TypeMuxSubscription{ - mux: mux, - created: time.Now(), - readC: c, - postC: c, - closing: make(chan struct{}), - } -} - -func (s *TypeMuxSubscription) Chan() <-chan *TypeMuxEvent { - return s.readC -} - -func (s *TypeMuxSubscription) Unsubscribe() { - s.mux.del(s) - s.closewait() -} - -func (s *TypeMuxSubscription) Closed() bool { - s.closeMu.Lock() - defer s.closeMu.Unlock() - return s.closed -} - -func (s *TypeMuxSubscription) closewait() { - s.closeMu.Lock() - defer s.closeMu.Unlock() - if s.closed { - return - } - close(s.closing) - s.closed = true - - s.postMu.Lock() - close(s.postC) - s.postC = nil - s.postMu.Unlock() -} - -func (s *TypeMuxSubscription) deliver(event *TypeMuxEvent) { - // Short circuit delivery if stale event - if s.created.After(event.Time) { - return - } - // Otherwise deliver the event - s.postMu.RLock() - defer s.postMu.RUnlock() - - select { - case s.postC <- event: - case <-s.closing: - } -} diff --git a/event/event_test.go b/event/event_test.go deleted file mode 100644 index 2be357ba..00000000 --- a/event/event_test.go +++ /dev/null @@ -1,218 +0,0 @@ -// 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 event - -import ( - "math/rand" - "sync" - "testing" - "time" -) - -type testEvent int - -func TestSubCloseUnsub(t *testing.T) { - // the point of this test is **not** to panic - var mux TypeMux - mux.Stop() - sub := mux.Subscribe(int(0)) - sub.Unsubscribe() -} - -func TestSub(t *testing.T) { - mux := new(TypeMux) - defer mux.Stop() - - sub := mux.Subscribe(testEvent(0)) - go func() { - if err := mux.Post(testEvent(5)); err != nil { - t.Errorf("Post returned unexpected error: %v", err) - } - }() - ev := <-sub.Chan() - - if ev.Data.(testEvent) != testEvent(5) { - t.Errorf("Got %v (%T), expected event %v (%T)", - ev, ev, testEvent(5), testEvent(5)) - } -} - -func TestMuxErrorAfterStop(t *testing.T) { - mux := new(TypeMux) - mux.Stop() - - sub := mux.Subscribe(testEvent(0)) - if _, isopen := <-sub.Chan(); isopen { - t.Errorf("subscription channel was not closed") - } - if err := mux.Post(testEvent(0)); err != ErrMuxClosed { - t.Errorf("Post error mismatch, got: %s, expected: %s", err, ErrMuxClosed) - } -} - -func TestUnsubscribeUnblockPost(t *testing.T) { - mux := new(TypeMux) - defer mux.Stop() - - sub := mux.Subscribe(testEvent(0)) - unblocked := make(chan bool) - go func() { - mux.Post(testEvent(5)) - unblocked <- true - }() - - select { - case <-unblocked: - t.Errorf("Post returned before Unsubscribe") - default: - sub.Unsubscribe() - <-unblocked - } -} - -func TestSubscribeDuplicateType(t *testing.T) { - mux := new(TypeMux) - expected := "event: duplicate type event.testEvent in Subscribe" - - defer func() { - err := recover() - if err == nil { - t.Errorf("Subscribe didn't panic for duplicate type") - } else if err != expected { - t.Errorf("panic mismatch: got %#v, expected %#v", err, expected) - } - }() - mux.Subscribe(testEvent(1), testEvent(2)) -} - -func TestMuxConcurrent(t *testing.T) { - rand.Seed(time.Now().Unix()) - mux := new(TypeMux) - defer mux.Stop() - - recv := make(chan int) - poster := func() { - for { - err := mux.Post(testEvent(0)) - if err != nil { - return - } - } - } - sub := func(i int) { - time.Sleep(time.Duration(rand.Intn(99)) * time.Millisecond) - sub := mux.Subscribe(testEvent(0)) - <-sub.Chan() - sub.Unsubscribe() - recv <- i - } - - go poster() - go poster() - go poster() - nsubs := 1000 - for i := 0; i < nsubs; i++ { - go sub(i) - } - - // wait until everyone has been served - counts := make(map[int]int, nsubs) - for i := 0; i < nsubs; i++ { - counts[<-recv]++ - } - for i, count := range counts { - if count != 1 { - t.Errorf("receiver %d called %d times, expected only 1 call", i, count) - } - } -} - -func emptySubscriber(mux *TypeMux) { - s := mux.Subscribe(testEvent(0)) - go func() { - for range s.Chan() { - } - }() -} - -func BenchmarkPost1000(b *testing.B) { - var ( - mux = new(TypeMux) - subscribed, done sync.WaitGroup - nsubs = 1000 - ) - subscribed.Add(nsubs) - done.Add(nsubs) - for i := 0; i < nsubs; i++ { - go func() { - s := mux.Subscribe(testEvent(0)) - subscribed.Done() - for range s.Chan() { - } - done.Done() - }() - } - subscribed.Wait() - - // The actual benchmark. - b.ResetTimer() - for i := 0; i < b.N; i++ { - mux.Post(testEvent(0)) - } - - b.StopTimer() - mux.Stop() - done.Wait() -} - -func BenchmarkPostConcurrent(b *testing.B) { - var mux = new(TypeMux) - defer mux.Stop() - emptySubscriber(mux) - emptySubscriber(mux) - emptySubscriber(mux) - - var wg sync.WaitGroup - poster := func() { - for i := 0; i < b.N; i++ { - mux.Post(testEvent(0)) - } - wg.Done() - } - wg.Add(5) - for i := 0; i < 5; i++ { - go poster() - } - wg.Wait() -} - -// for comparison -func BenchmarkChanSend(b *testing.B) { - c := make(chan interface{}) - closed := make(chan struct{}) - go func() { - for range c { - } - }() - - for i := 0; i < b.N; i++ { - select { - case c <- i: - case <-closed: - } - } -} diff --git a/event/example_feed_test.go b/event/example_feed_test.go deleted file mode 100644 index 9b5ad50d..00000000 --- a/event/example_feed_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// 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_test - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/event" -) - -func ExampleFeed_acknowledgedEvents() { - // This example shows how the return value of Send can be used for request/reply - // interaction between event consumers and producers. - var feed event.Feed - type ackedEvent struct { - i int - ack chan<- struct{} - } - - // Consumers wait for events on the feed and acknowledge processing. - done := make(chan struct{}) - defer close(done) - for i := 0; i < 3; i++ { - ch := make(chan ackedEvent, 100) - sub := feed.Subscribe(ch) - go func() { - defer sub.Unsubscribe() - for { - select { - case ev := <-ch: - fmt.Println(ev.i) // "process" the event - ev.ack <- struct{}{} - case <-done: - return - } - } - }() - } - - // The producer sends values of type ackedEvent with increasing values of i. - // It waits for all consumers to acknowledge before sending the next event. - for i := 0; i < 3; i++ { - acksignal := make(chan struct{}) - n := feed.Send(ackedEvent{i, acksignal}) - for ack := 0; ack < n; ack++ { - <-acksignal - } - } - // Output: - // 0 - // 0 - // 0 - // 1 - // 1 - // 1 - // 2 - // 2 - // 2 -} diff --git a/event/example_scope_test.go b/event/example_scope_test.go deleted file mode 100644 index 825a8dee..00000000 --- a/event/example_scope_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// 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_test - -import ( - "fmt" - "sync" - - "github.com/ethereum/go-ethereum/event" -) - -// This example demonstrates how SubscriptionScope can be used to control the lifetime of -// subscriptions. -// -// Our example program consists of two servers, each of which performs a calculation when -// requested. The servers also allow subscribing to results of all computations. -type divServer struct{ results event.Feed } -type mulServer struct{ results event.Feed } - -func (s *divServer) do(a, b int) int { - r := a / b - s.results.Send(r) - return r -} - -func (s *mulServer) do(a, b int) int { - r := a * b - s.results.Send(r) - return r -} - -// The servers are contained in an App. The app controls the servers and exposes them -// through its API. -type App struct { - divServer - mulServer - scope event.SubscriptionScope -} - -func (s *App) Calc(op byte, a, b int) int { - switch op { - case '/': - return s.divServer.do(a, b) - case '*': - return s.mulServer.do(a, b) - default: - panic("invalid op") - } -} - -// The app's SubscribeResults method starts sending calculation results to the given -// channel. Subscriptions created through this method are tied to the lifetime of the App -// because they are registered in the scope. -func (s *App) SubscribeResults(op byte, ch chan<- int) event.Subscription { - switch op { - case '/': - return s.scope.Track(s.divServer.results.Subscribe(ch)) - case '*': - return s.scope.Track(s.mulServer.results.Subscribe(ch)) - default: - panic("invalid op") - } -} - -// Stop stops the App, closing all subscriptions created through SubscribeResults. -func (s *App) Stop() { - s.scope.Close() -} - -func ExampleSubscriptionScope() { - // Create the app. - var ( - app App - wg sync.WaitGroup - divs = make(chan int) - muls = make(chan int) - ) - - // Run a subscriber in the background. - divsub := app.SubscribeResults('/', divs) - mulsub := app.SubscribeResults('*', muls) - wg.Add(1) - go func() { - defer wg.Done() - defer fmt.Println("subscriber exited") - defer divsub.Unsubscribe() - defer mulsub.Unsubscribe() - for { - select { - case result := <-divs: - fmt.Println("division happened:", result) - case result := <-muls: - fmt.Println("multiplication happened:", result) - case <-divsub.Err(): - return - case <-mulsub.Err(): - return - } - } - }() - - // Interact with the app. - app.Calc('/', 22, 11) - app.Calc('*', 3, 4) - - // Stop the app. This shuts down the subscriptions, causing the subscriber to exit. - app.Stop() - wg.Wait() - - // Output: - // division happened: 2 - // multiplication happened: 12 - // subscriber exited -} diff --git a/event/example_subscription_test.go b/event/example_subscription_test.go deleted file mode 100644 index 5c76b55d..00000000 --- a/event/example_subscription_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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_test - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/event" -) - -func ExampleNewSubscription() { - // Create a subscription that sends 10 integers on ch. - ch := make(chan int) - sub := event.NewSubscription(func(quit <-chan struct{}) error { - for i := 0; i < 10; i++ { - select { - case ch <- i: - case <-quit: - fmt.Println("unsubscribed") - return nil - } - } - return nil - }) - - // This is the consumer. It reads 5 integers, then aborts the subscription. - // Note that Unsubscribe waits until the producer has shut down. - for i := range ch { - fmt.Println(i) - if i == 4 { - sub.Unsubscribe() - break - } - } - // Output: - // 0 - // 1 - // 2 - // 3 - // 4 - // unsubscribed -} diff --git a/event/example_test.go b/event/example_test.go deleted file mode 100644 index 29938e85..00000000 --- a/event/example_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// 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 event - -import "fmt" - -func ExampleTypeMux() { - type someEvent struct{ I int } - type otherEvent struct{ S string } - type yetAnotherEvent struct{ X, Y int } - - var mux TypeMux - - // Start a subscriber. - done := make(chan struct{}) - sub := mux.Subscribe(someEvent{}, otherEvent{}) - go func() { - for event := range sub.Chan() { - fmt.Printf("Received: %#v\n", event.Data) - } - fmt.Println("done") - close(done) - }() - - // Post some events. - mux.Post(someEvent{5}) - mux.Post(yetAnotherEvent{X: 3, Y: 4}) - mux.Post(someEvent{6}) - mux.Post(otherEvent{"whoa"}) - - // Stop closes all subscription channels. - // The subscriber goroutine will print "done" - // and exit. - mux.Stop() - - // Wait for subscriber to return. - <-done - - // Output: - // Received: event.someEvent{I:5} - // Received: event.someEvent{I:6} - // Received: event.otherEvent{S:"whoa"} - // done -} diff --git a/event/feed.go b/event/feed.go deleted file mode 100644 index 02f3ca68..00000000 --- a/event/feed.go +++ /dev/null @@ -1,247 +0,0 @@ -// 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 ( - "errors" - "reflect" - "sync" -) - -var errBadChannel = errors.New("event: Subscribe argument does not have sendable channel type") - -// Feed implements one-to-many subscriptions where the carrier of events is a channel. -// Values sent to a Feed are delivered to all subscribed channels simultaneously. -// -// Feeds can only be used with a single type. The type is determined by the first Send or -// Subscribe operation. Subsequent calls to these methods panic if the type does not -// match. -// -// The zero value is ready to use. -type Feed struct { - once sync.Once // ensures that init only runs once - sendLock chan struct{} // sendLock has a one-element buffer and is empty when held.It protects sendCases. - removeSub chan interface{} // interrupts Send - sendCases caseList // the active set of select cases used by Send - - // The inbox holds newly subscribed channels until they are added to sendCases. - mu sync.Mutex - inbox caseList - etype reflect.Type -} - -// This is the index of the first actual subscription channel in sendCases. -// sendCases[0] is a SelectRecv case for the removeSub channel. -const firstSubSendCase = 1 - -type feedTypeError struct { - got, want reflect.Type - op string -} - -func (e feedTypeError) Error() string { - return "event: wrong type in " + e.op + " got " + e.got.String() + ", want " + e.want.String() -} - -func (f *Feed) init() { - f.removeSub = make(chan interface{}) - f.sendLock = make(chan struct{}, 1) - f.sendLock <- struct{}{} - f.sendCases = caseList{{Chan: reflect.ValueOf(f.removeSub), Dir: reflect.SelectRecv}} -} - -// Subscribe adds a channel to the feed. Future sends will be delivered on the channel -// until the subscription is canceled. All channels added must have the same element type. -// -// The channel should have ample buffer space to avoid blocking other subscribers. -// Slow subscribers are not dropped. -func (f *Feed) Subscribe(channel interface{}) Subscription { - f.once.Do(f.init) - - chanval := reflect.ValueOf(channel) - chantyp := chanval.Type() - if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.SendDir == 0 { - panic(errBadChannel) - } - sub := &feedSub{feed: f, channel: chanval, err: make(chan error, 1)} - - f.mu.Lock() - defer f.mu.Unlock() - if !f.typecheck(chantyp.Elem()) { - panic(feedTypeError{op: "Subscribe", got: chantyp, want: reflect.ChanOf(reflect.SendDir, f.etype)}) - } - // Add the select case to the inbox. - // The next Send will add it to f.sendCases. - cas := reflect.SelectCase{Dir: reflect.SelectSend, Chan: chanval} - f.inbox = append(f.inbox, cas) - return sub -} - -// note: callers must hold f.mu -func (f *Feed) typecheck(typ reflect.Type) bool { - if f.etype == nil { - f.etype = typ - return true - } - return f.etype == typ -} - -func (f *Feed) remove(sub *feedSub) { - // Delete from inbox first, which covers channels - // that have not been added to f.sendCases yet. - ch := sub.channel.Interface() - f.mu.Lock() - index := f.inbox.find(ch) - if index != -1 { - f.inbox = f.inbox.delete(index) - f.mu.Unlock() - return - } - f.mu.Unlock() - - select { - case f.removeSub <- ch: - // Send will remove the channel from f.sendCases. - case <-f.sendLock: - // No Send is in progress, delete the channel now that we have the send lock. - f.sendCases = f.sendCases.delete(f.sendCases.find(ch)) - f.sendLock <- struct{}{} - } -} - -// Send delivers to all subscribed channels simultaneously. -// It returns the number of subscribers that the value was sent to. -func (f *Feed) Send(value interface{}) (nsent int) { - rvalue := reflect.ValueOf(value) - - f.once.Do(f.init) - <-f.sendLock - - // Add new cases from the inbox after taking the send lock. - f.mu.Lock() - f.sendCases = append(f.sendCases, f.inbox...) - f.inbox = nil - - if !f.typecheck(rvalue.Type()) { - f.sendLock <- struct{}{} - panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype}) - } - f.mu.Unlock() - - // Set the sent value on all channels. - for i := firstSubSendCase; i < len(f.sendCases); i++ { - f.sendCases[i].Send = rvalue - } - - // Send until all channels except removeSub have been chosen. 'cases' tracks a prefix - // of sendCases. When a send succeeds, the corresponding case moves to the end of - // 'cases' and it shrinks by one element. - cases := f.sendCases - for { - // Fast path: try sending without blocking before adding to the select set. - // This should usually succeed if subscribers are fast enough and have free - // buffer space. - for i := firstSubSendCase; i < len(cases); i++ { - if cases[i].Chan.TrySend(rvalue) { - nsent++ - cases = cases.deactivate(i) - i-- - } - } - if len(cases) == firstSubSendCase { - break - } - // Select on all the receivers, waiting for them to unblock. - chosen, recv, _ := reflect.Select(cases) - if chosen == 0 /* <-f.removeSub */ { - index := f.sendCases.find(recv.Interface()) - f.sendCases = f.sendCases.delete(index) - if index >= 0 && index < len(cases) { - // Shrink 'cases' too because the removed case was still active. - cases = f.sendCases[:len(cases)-1] - } - } else { - cases = cases.deactivate(chosen) - nsent++ - } - } - - // Forget about the sent value and hand off the send lock. - for i := firstSubSendCase; i < len(f.sendCases); i++ { - f.sendCases[i].Send = reflect.Value{} - } - f.sendLock <- struct{}{} - return nsent -} - -type feedSub struct { - feed *Feed - channel reflect.Value - errOnce sync.Once - err chan error -} - -func (sub *feedSub) Unsubscribe() { - sub.errOnce.Do(func() { - sub.feed.remove(sub) - close(sub.err) - }) -} - -func (sub *feedSub) Err() <-chan error { - return sub.err -} - -type caseList []reflect.SelectCase - -// find returns the index of a case containing the given channel. -func (cs caseList) find(channel interface{}) int { - for i, cas := range cs { - if cas.Chan.Interface() == channel { - return i - } - } - return -1 -} - -// delete removes the given case from cs. -func (cs caseList) delete(index int) caseList { - return append(cs[:index], cs[index+1:]...) -} - -// deactivate moves the case at index into the non-accessible portion of the cs slice. -func (cs caseList) deactivate(index int) caseList { - last := len(cs) - 1 - cs[index], cs[last] = cs[last], cs[index] - return cs[:last] -} - -// func (cs caseList) String() string { -// s := "[" -// for i, cas := range cs { -// if i != 0 { -// s += ", " -// } -// switch cas.Dir { -// case reflect.SelectSend: -// s += fmt.Sprintf("%v<-", cas.Chan.Interface()) -// case reflect.SelectRecv: -// s += fmt.Sprintf("<-%v", cas.Chan.Interface()) -// } -// } -// return s + "]" -// } diff --git a/event/feed_test.go b/event/feed_test.go deleted file mode 100644 index be887693..00000000 --- a/event/feed_test.go +++ /dev/null @@ -1,333 +0,0 @@ -// 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 ( - "fmt" - "reflect" - "sync" - "testing" - "time" -) - -func TestFeedPanics(t *testing.T) { - { - var f Feed - f.Send(int(2)) - want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))} - if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil { - t.Error(err) - } - } - { - var f Feed - ch := make(chan int) - f.Subscribe(ch) - want := feedTypeError{op: "Send", got: reflect.TypeOf(uint64(0)), want: reflect.TypeOf(int(0))} - if err := checkPanic(want, func() { f.Send(uint64(2)) }); err != nil { - t.Error(err) - } - } - { - var f Feed - f.Send(int(2)) - want := feedTypeError{op: "Subscribe", got: reflect.TypeOf(make(chan uint64)), want: reflect.TypeOf(make(chan<- int))} - if err := checkPanic(want, func() { f.Subscribe(make(chan uint64)) }); err != nil { - t.Error(err) - } - } - { - var f Feed - if err := checkPanic(errBadChannel, func() { f.Subscribe(make(<-chan int)) }); err != nil { - t.Error(err) - } - } - { - var f Feed - if err := checkPanic(errBadChannel, func() { f.Subscribe(int(0)) }); err != nil { - t.Error(err) - } - } -} - -func checkPanic(want error, fn func()) (err error) { - defer func() { - panic := recover() - if panic == nil { - err = fmt.Errorf("didn't panic") - } else if !reflect.DeepEqual(panic, want) { - err = fmt.Errorf("panicked with wrong error: got %q, want %q", panic, want) - } - }() - fn() - return nil -} - -func TestFeed(t *testing.T) { - var feed Feed - var done, subscribed sync.WaitGroup - subscriber := func(i int) { - defer done.Done() - - subchan := make(chan int) - sub := feed.Subscribe(subchan) - timeout := time.NewTimer(2 * time.Second) - subscribed.Done() - - select { - case v := <-subchan: - if v != 1 { - t.Errorf("%d: received value %d, want 1", i, v) - } - case <-timeout.C: - t.Errorf("%d: receive timeout", i) - } - - sub.Unsubscribe() - select { - case _, ok := <-sub.Err(): - if ok { - t.Errorf("%d: error channel not closed after unsubscribe", i) - } - case <-timeout.C: - t.Errorf("%d: unsubscribe timeout", i) - } - } - - const n = 1000 - done.Add(n) - subscribed.Add(n) - for i := 0; i < n; i++ { - go subscriber(i) - } - subscribed.Wait() - if nsent := feed.Send(1); nsent != n { - t.Errorf("first send delivered %d times, want %d", nsent, n) - } - if nsent := feed.Send(2); nsent != 0 { - t.Errorf("second send delivered %d times, want 0", nsent) - } - done.Wait() -} - -func TestFeedSubscribeSameChannel(t *testing.T) { - var ( - feed Feed - done sync.WaitGroup - ch = make(chan int) - sub1 = feed.Subscribe(ch) - sub2 = feed.Subscribe(ch) - _ = feed.Subscribe(ch) - ) - expectSends := func(value, n int) { - if nsent := feed.Send(value); nsent != n { - t.Errorf("send delivered %d times, want %d", nsent, n) - } - done.Done() - } - expectRecv := func(wantValue, n int) { - for i := 0; i < n; i++ { - if v := <-ch; v != wantValue { - t.Errorf("received %d, want %d", v, wantValue) - } - } - } - - done.Add(1) - go expectSends(1, 3) - expectRecv(1, 3) - done.Wait() - - sub1.Unsubscribe() - - done.Add(1) - go expectSends(2, 2) - expectRecv(2, 2) - done.Wait() - - sub2.Unsubscribe() - - done.Add(1) - go expectSends(3, 1) - expectRecv(3, 1) - done.Wait() -} - -func TestFeedSubscribeBlockedPost(t *testing.T) { - var ( - feed Feed - nsends = 2000 - ch1 = make(chan int) - ch2 = make(chan int) - wg sync.WaitGroup - ) - defer wg.Wait() - - feed.Subscribe(ch1) - wg.Add(nsends) - for i := 0; i < nsends; i++ { - go func() { - feed.Send(99) - wg.Done() - }() - } - - sub2 := feed.Subscribe(ch2) - defer sub2.Unsubscribe() - - // We're done when ch1 has received N times. - // The number of receives on ch2 depends on scheduling. - for i := 0; i < nsends; { - select { - case <-ch1: - i++ - case <-ch2: - } - } -} - -func TestFeedUnsubscribeBlockedPost(t *testing.T) { - var ( - feed Feed - nsends = 200 - chans = make([]chan int, 2000) - subs = make([]Subscription, len(chans)) - bchan = make(chan int) - bsub = feed.Subscribe(bchan) - wg sync.WaitGroup - ) - for i := range chans { - chans[i] = make(chan int, nsends) - } - - // Queue up some Sends. None of these can make progress while bchan isn't read. - wg.Add(nsends) - for i := 0; i < nsends; i++ { - go func() { - feed.Send(99) - wg.Done() - }() - } - // Subscribe the other channels. - for i, ch := range chans { - subs[i] = feed.Subscribe(ch) - } - // Unsubscribe them again. - for _, sub := range subs { - sub.Unsubscribe() - } - // Unblock the Sends. - bsub.Unsubscribe() - wg.Wait() -} - -// Checks that unsubscribing a channel during Send works even if that -// channel has already been sent on. -func TestFeedUnsubscribeSentChan(t *testing.T) { - var ( - feed Feed - ch1 = make(chan int) - ch2 = make(chan int) - sub1 = feed.Subscribe(ch1) - sub2 = feed.Subscribe(ch2) - wg sync.WaitGroup - ) - defer sub2.Unsubscribe() - - wg.Add(1) - go func() { - feed.Send(0) - wg.Done() - }() - - // Wait for the value on ch1. - <-ch1 - // Unsubscribe ch1, removing it from the send cases. - sub1.Unsubscribe() - - // Receive ch2, finishing Send. - <-ch2 - wg.Wait() - - // Send again. This should send to ch2 only, so the wait group will unblock - // as soon as a value is received on ch2. - wg.Add(1) - go func() { - feed.Send(0) - wg.Done() - }() - <-ch2 - wg.Wait() -} - -func TestFeedUnsubscribeFromInbox(t *testing.T) { - var ( - feed Feed - ch1 = make(chan int) - ch2 = make(chan int) - sub1 = feed.Subscribe(ch1) - sub2 = feed.Subscribe(ch1) - sub3 = feed.Subscribe(ch2) - ) - if len(feed.inbox) != 3 { - t.Errorf("inbox length != 3 after subscribe") - } - if len(feed.sendCases) != 1 { - t.Errorf("sendCases is non-empty after unsubscribe") - } - - sub1.Unsubscribe() - sub2.Unsubscribe() - sub3.Unsubscribe() - if len(feed.inbox) != 0 { - t.Errorf("inbox is non-empty after unsubscribe") - } - if len(feed.sendCases) != 1 { - t.Errorf("sendCases is non-empty after unsubscribe") - } -} - -func BenchmarkFeedSend1000(b *testing.B) { - var ( - done sync.WaitGroup - feed Feed - nsubs = 1000 - ) - subscriber := func(ch <-chan int) { - for i := 0; i < b.N; i++ { - <-ch - } - done.Done() - } - done.Add(nsubs) - for i := 0; i < nsubs; i++ { - ch := make(chan int, 200) - feed.Subscribe(ch) - go subscriber(ch) - } - - // The actual benchmark. - b.ResetTimer() - for i := 0; i < b.N; i++ { - if feed.Send(i) != nsubs { - panic("wrong number of sends") - } - } - - b.StopTimer() - done.Wait() -} diff --git a/event/subscription.go b/event/subscription.go deleted file mode 100644 index d03f4650..00000000 --- a/event/subscription.go +++ /dev/null @@ -1,275 +0,0 @@ -// 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 ( - "context" - "sync" - "time" - - "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 { - s := &resubscribeSub{ - waitTime: backoffMax / 10, - backoffMax: backoffMax, - fn: fn, - err: make(chan error), - unsub: make(chan struct{}), - } - go s.loop() - return s -} - -// A ResubscribeFunc attempts to establish a subscription. -type ResubscribeFunc func(context.Context) (Subscription, error) - -type resubscribeSub struct { - fn ResubscribeFunc - err chan error - unsub chan struct{} - unsubOnce sync.Once - lastTry mclock.AbsTime - 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 -retry: - for { - s.lastTry = mclock.Now() - ctx, cancel := context.WithCancel(context.Background()) - go func() { - rsub, err := s.fn(ctx) - sub = rsub - subscribed <- err - }() - select { - case err := <-subscribed: - cancel() - if err != nil { - // Subscribing failed, wait before launching the next try. - if s.backoffWait() { - return nil - } - continue retry - } - if sub == nil { - panic("event: ResubscribeFunc returned nil subscription and no error") - } - return sub - case <-s.unsub: - cancel() - return nil - } - } -} - -func (s *resubscribeSub) waitForError(sub Subscription) bool { - defer sub.Unsubscribe() - select { - case err := <-sub.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/event/subscription_test.go b/event/subscription_test.go deleted file mode 100644 index 5b8a2c8e..00000000 --- a/event/subscription_test.go +++ /dev/null @@ -1,120 +0,0 @@ -// 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 ( - "context" - "errors" - "testing" - "time" -) - -var errInts = errors.New("error in subscribeInts") - -func subscribeInts(max, fail int, c chan<- int) Subscription { - return NewSubscription(func(quit <-chan struct{}) error { - for i := 0; i < max; i++ { - if i >= fail { - return errInts - } - select { - case c <- i: - case <-quit: - return nil - } - } - return nil - }) -} - -func TestNewSubscriptionError(t *testing.T) { - t.Parallel() - - channel := make(chan int) - sub := subscribeInts(10, 2, channel) -loop: - for want := 0; want < 10; want++ { - select { - case got := <-channel: - if got != want { - t.Fatalf("wrong int %d, want %d", got, want) - } - case err := <-sub.Err(): - if err != errInts { - t.Fatalf("wrong error: got %q, want %q", err, errInts) - } - if want != 2 { - t.Fatalf("got errInts at int %d, should be received at 2", want) - } - break loop - } - } - sub.Unsubscribe() - - err, ok := <-sub.Err() - if err != nil { - t.Fatal("got non-nil error after Unsubscribe") - } - if ok { - t.Fatal("channel still open after Unsubscribe") - } -} - -func TestResubscribe(t *testing.T) { - t.Parallel() - - var i int - nfails := 6 - sub := Resubscribe(100*time.Millisecond, func(ctx context.Context) (Subscription, error) { - // fmt.Printf("call #%d @ %v\n", i, time.Now()) - i++ - if i == 2 { - // Delay the second failure a bit to reset the resubscribe interval. - time.Sleep(200 * time.Millisecond) - } - if i < nfails { - return nil, errors.New("oops") - } - sub := NewSubscription(func(unsubscribed <-chan struct{}) error { return nil }) - return sub, nil - }) - - <-sub.Err() - if i != nfails { - t.Fatalf("resubscribe function called %d times, want %d times", i, nfails) - } -} - -func TestResubscribeAbort(t *testing.T) { - t.Parallel() - - done := make(chan error) - sub := Resubscribe(0, func(ctx context.Context) (Subscription, error) { - select { - case <-ctx.Done(): - done <- nil - case <-time.After(2 * time.Second): - done <- errors.New("context given to resubscribe function not canceled within 2s") - } - return nil, nil - }) - - sub.Unsubscribe() - if err := <-done; err != nil { - t.Fatal(err) - } -} diff --git a/examples/KVTableTest.go b/examples/KVTableTest.go index 455a355b..f89ae9ad 100644 --- a/examples/KVTableTest.go +++ b/examples/KVTableTest.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // KVTableTestABI is the input ABI used to generate the binding from. @@ -280,115 +278,12 @@ type KVTableTestSetResultIterator struct { 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 *KVTableTestSetResultIterator) 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(KVTableTestSetResult) - 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(KVTableTestSetResult) - 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 *KVTableTestSetResultIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *KVTableTestSetResultIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - // KVTableTestSetResult represents a SetResult event raised by the KVTableTest contract. type KVTableTestSetResult struct { Count *big.Int Raw types.Log // Blockchain specific contextual infos } -// FilterSetResult is a free log retrieval operation binding the contract event 0xb103249d88cd818b10c5cd6889874103a7699c5834cb078d8f35925dca8a62d6. -// -// Solidity: event SetResult(int256 count) -func (_KVTableTest *KVTableTestFilterer) FilterSetResult(opts *bind.FilterOpts) (*KVTableTestSetResultIterator, error) { - - logs, sub, err := _KVTableTest.contract.FilterLogs(opts, "SetResult") - if err != nil { - return nil, err - } - return &KVTableTestSetResultIterator{contract: _KVTableTest.contract, event: "SetResult", logs: logs, sub: sub}, nil -} - -// WatchSetResult is a free log subscription operation binding the contract event 0xb103249d88cd818b10c5cd6889874103a7699c5834cb078d8f35925dca8a62d6. -// -// Solidity: event SetResult(int256 count) -func (_KVTableTest *KVTableTestFilterer) WatchSetResult(opts *bind.WatchOpts, sink chan<- *KVTableTestSetResult) (event.Subscription, error) { - - logs, sub, err := _KVTableTest.contract.WatchLogs(opts, "SetResult") - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(KVTableTestSetResult) - if err := _KVTableTest.contract.UnpackLog(event, "SetResult", 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 -} - // ParseSetResult is a log parse operation binding the contract event 0xb103249d88cd818b10c5cd6889874103a7699c5834cb078d8f35925dca8a62d6. // // Solidity: event SetResult(int256 count) diff --git a/examples/amop/broadcast_pub/publisher.go b/examples/amop/broadcast_pub/publisher.go index b95d94d4..1c1ed905 100644 --- a/examples/amop/broadcast_pub/publisher.go +++ b/examples/amop/broadcast_pub/publisher.go @@ -2,24 +2,24 @@ package main import ( "encoding/hex" - "log" "os" "strconv" "time" "github.com/FISCO-BCOS/go-sdk/client" "github.com/FISCO-BCOS/go-sdk/conf" + "github.com/sirupsen/logrus" ) func main() { if len(os.Args) < 3 { - log.Fatal("the number of arguments is not equal 3") + logrus.Fatal("the number of arguments is not equal 3") } waitToSend := 5 * time.Second if len(os.Args) == 4 { i, err := strconv.Atoi(os.Args[3]) if err != nil { - log.Fatalf("parse to int failed: %v", err) + logrus.Fatalf("parse to int failed: %v", err) } waitToSend = time.Duration(i) * time.Second } @@ -31,17 +31,17 @@ func main() { NodeURL: endpoint} c, err := client.Dial(config) if err != nil { - log.Fatalf("init publisher failed, err: %v\n", err) + logrus.Fatalf("init publisher failed, err: %v\n", err) } time.Sleep(waitToSend) message := "hello, FISCO BCOS, I am broadcast publisher!" for i := 0; i < 50; i++ { - log.Printf("publish message: %s ", message+" "+strconv.Itoa(i)) + logrus.Printf("publish message: %s ", message+" "+strconv.Itoa(i)) err = c.BroadcastAMOPMsg(topic, []byte(message+" "+strconv.Itoa(i))) time.Sleep(200 * time.Millisecond) if err != nil { - log.Printf("PushTopicDataRandom failed, err: %v\n", err) + logrus.Printf("PushTopicDataRandom failed, err: %v\n", err) } } c.BroadcastAMOPMsg(topic, []byte("Done")) diff --git a/examples/amop/sub/subscriber.go b/examples/amop/sub/subscriber.go index e22c5ca4..1787f9e4 100644 --- a/examples/amop/sub/subscriber.go +++ b/examples/amop/sub/subscriber.go @@ -2,7 +2,6 @@ package main import ( "encoding/hex" - "log" "os" "os/signal" "strings" @@ -10,11 +9,12 @@ import ( "github.com/FISCO-BCOS/go-sdk/client" "github.com/FISCO-BCOS/go-sdk/conf" + "github.com/sirupsen/logrus" ) func main() { if len(os.Args) < 3 { - log.Fatalf("parameters are not enough, example \n%s 127.0.0.1:20202 hello", os.Args[0]) + logrus.Fatalf("parameters are not enough, example \n%s 127.0.0.1:20202 hello", os.Args[0]) } endpoint := os.Args[1] topic := os.Args[2] @@ -24,23 +24,23 @@ func main() { var c *client.Client var err error for i := 0; i < 3; i++ { - log.Printf("%d try to connect\n", i) + logrus.Printf("%d try to connect\n", i) c, err = client.Dial(config) if err != nil { - log.Printf("init subscriber failed, err: %v, retrying\n", err) + logrus.Printf("init subscriber failed, err: %v, retrying\n", err) continue } break } if err != nil { - log.Fatalf("init subscriber failed, err: %v\n", err) + logrus.Fatalf("init subscriber failed, err: %v\n", err) } timeout := 10 * time.Second queryTicker := time.NewTicker(timeout) defer queryTicker.Stop() done := make(chan bool) err = c.SubscribeTopic(topic, func(data []byte, response *[]byte) { - log.Printf("received: %s\n", string(data)) + logrus.Printf("received: %s\n", string(data)) queryTicker.Stop() if strings.Contains(string(data), "Done") { done <- true @@ -49,23 +49,23 @@ func main() { queryTicker = time.NewTicker(timeout) }) if err != nil { - log.Printf("SubscribeAuthTopic failed, err: %v\n", err) + logrus.Printf("SubscribeAuthTopic failed, err: %v\n", err) return } - log.Printf("Subscriber %s success %s\n", topic, time.Now().String()) + logrus.Printf("Subscriber %s success %s\n", topic, time.Now().String()) killSignal := make(chan os.Signal, 1) signal.Notify(killSignal, os.Interrupt) for { select { case <-done: - log.Println("Done!") + logrus.Println("Done!") os.Exit(0) case <-queryTicker.C: - log.Printf("can't receive message after 10s, %s\n", time.Now().String()) + logrus.Printf("can't receive message after 10s, %s\n", time.Now().String()) os.Exit(1) case <-killSignal: - log.Println("user exit") + logrus.Println("user exit") os.Exit(0) } } diff --git a/examples/amop/unicast_pub/publisher.go b/examples/amop/unicast_pub/publisher.go index e30daffb..528de294 100644 --- a/examples/amop/unicast_pub/publisher.go +++ b/examples/amop/unicast_pub/publisher.go @@ -2,24 +2,24 @@ package main import ( "encoding/hex" - "log" "os" "strconv" "time" "github.com/FISCO-BCOS/go-sdk/client" "github.com/FISCO-BCOS/go-sdk/conf" + "github.com/sirupsen/logrus" ) func main() { if len(os.Args) < 3 { - log.Fatal("the number of arguments is not equal 3") + logrus.Fatal("the number of arguments is not equal 3") } waitToSend := 5 * time.Second if len(os.Args) == 4 { i, err := strconv.Atoi(os.Args[3]) if err != nil { - log.Fatalf("parse to int failed: %v", err) + logrus.Fatalf("parse to int failed: %v", err) } waitToSend = time.Duration(i) * time.Second } @@ -30,17 +30,17 @@ func main() { IsSMCrypto: false, GroupID: 1, PrivateKey: privateKey, NodeURL: endpoint} c, err := client.Dial(config) if err != nil { - log.Fatalf("init publisher failed, err: %v\n", err) + logrus.Fatalf("init publisher failed, err: %v\n", err) } time.Sleep(waitToSend) message := "hello, FISCO BCOS, I am unicast publisher!" for i := 0; i < 50; i++ { - log.Printf("publish message: %s ", message+" "+strconv.Itoa(i)) + logrus.Printf("publish message: %s ", message+" "+strconv.Itoa(i)) err = c.BroadcastAMOPMsg(topic, []byte(message+" "+strconv.Itoa(i))) time.Sleep(200 * time.Millisecond) if err != nil { - log.Printf("PushTopicDataRandom failed, err: %v\n", err) + logrus.Printf("PushTopicDataRandom failed, err: %v\n", err) } } c.BroadcastAMOPMsg(topic, []byte("Done")) diff --git a/examples/amop_auth/broadcast_pub/publisher.go b/examples/amop_auth/broadcast_pub/publisher.go index 35a0fb00..9e8b7235 100644 --- a/examples/amop_auth/broadcast_pub/publisher.go +++ b/examples/amop_auth/broadcast_pub/publisher.go @@ -4,7 +4,6 @@ import ( "crypto/ecdsa" "encoding/hex" "fmt" - "log" "os" "os/signal" "strconv" @@ -14,6 +13,7 @@ import ( "github.com/FISCO-BCOS/go-sdk/conf" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/sirupsen/logrus" ) func main() { @@ -26,16 +26,16 @@ func main() { for _, k := range publicKeysHex { pubKeyBytes, err := hexutil.Decode(k) if err != nil { - log.Fatalf("decode publicKey failed, err: %v\n", err) + logrus.Fatalf("decode publicKey failed, err: %v\n", err) } pubKey, err := crypto.UnmarshalPubkey(pubKeyBytes) if err != nil { - log.Fatalf("decompress pubkey failed, err: %v", err) + logrus.Fatalf("decompress pubkey failed, err: %v", err) } publicKeys = append(publicKeys, pubKey) } if len(os.Args) < 3 { - log.Fatal("the number of arguments is not equal 3") + logrus.Fatal("the number of arguments is not equal 3") } else if len(os.Args) > 3 { keys := 3 publicKeys = make([]*ecdsa.PublicKey, 0) @@ -61,12 +61,12 @@ func main() { IsSMCrypto: false, GroupID: 1, PrivateKey: privateKey, NodeURL: endpoint} c, err := client.Dial(config) if err != nil { - log.Fatalf("init publisher failed, err: %v\n", err) + logrus.Fatalf("init publisher failed, err: %v\n", err) } err = c.PublishPrivateTopic(topic, publicKeys) if err != nil { - log.Fatalf("publish topic failed, err: %v\n", err) + logrus.Fatalf("publish topic failed, err: %v\n", err) } fmt.Println("publish topic success") time.Sleep(3 * time.Second) @@ -74,11 +74,11 @@ func main() { message := "Hi, FISCO BCOS!" go func() { for i := 0; i < 1000; i++ { - log.Printf("publish message: %s ", message+" "+strconv.Itoa(i)) + logrus.Printf("publish message: %s ", message+" "+strconv.Itoa(i)) err = c.BroadcastAMOPPrivateMsg(topic, []byte(message+" "+strconv.Itoa(i))) time.Sleep(2 * time.Second) if err != nil { - log.Printf("PushTopicDataRandom failed, err: %v\n", err) + logrus.Printf("PushTopicDataRandom failed, err: %v\n", err) } } }() diff --git a/examples/amop_auth/sub/subscriber.go b/examples/amop_auth/sub/subscriber.go index c22273f9..087d6073 100644 --- a/examples/amop_auth/sub/subscriber.go +++ b/examples/amop_auth/sub/subscriber.go @@ -3,13 +3,13 @@ package main import ( "encoding/hex" "fmt" - "log" "os" "os/signal" "github.com/FISCO-BCOS/go-sdk/client" "github.com/FISCO-BCOS/go-sdk/conf" "github.com/ethereum/go-ethereum/crypto" + "github.com/sirupsen/logrus" ) const ( @@ -19,7 +19,7 @@ const ( ) func onPush(data []byte, response *[]byte) { - log.Printf("received: %s\n", string(data)) + logrus.Printf("received: %s\n", string(data)) } var ( @@ -41,7 +41,7 @@ func main() { return } } else if len(os.Args) < 3 { - log.Fatal("the number of arguments less than 3") + logrus.Fatal("the number of arguments less than 3") } endpoint := os.Args[1] @@ -51,12 +51,12 @@ func main() { IsSMCrypto: false, GroupID: 1, PrivateKey: signKey, NodeURL: endpoint} c, err := client.Dial(config) if err != nil { - log.Fatalf("init client failed, err: %v\n", err) + logrus.Fatalf("init client failed, err: %v\n", err) } err = c.SubscribePrivateTopic(topic, privateKey, onPush) if err != nil { - log.Fatalf("SubscribeAuthTopic failed, err: %v\n", err) + logrus.Fatalf("SubscribeAuthTopic failed, err: %v\n", err) } fmt.Println("Subscriber success") diff --git a/examples/amop_auth/unicast_pub/publisher.go b/examples/amop_auth/unicast_pub/publisher.go index bdb1eaf8..1d760fe1 100644 --- a/examples/amop_auth/unicast_pub/publisher.go +++ b/examples/amop_auth/unicast_pub/publisher.go @@ -4,7 +4,6 @@ import ( "crypto/ecdsa" "encoding/hex" "fmt" - "log" "os" "os/signal" "strconv" @@ -14,6 +13,7 @@ import ( "github.com/FISCO-BCOS/go-sdk/conf" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/sirupsen/logrus" ) func main() { @@ -26,16 +26,16 @@ func main() { for _, k := range publicKeysHex { pubKeyBytes, err := hexutil.Decode(k) if err != nil { - log.Fatalf("decode publicKey failed, err: %v\n", err) + logrus.Fatalf("decode publicKey failed, err: %v\n", err) } pubKey, err := crypto.UnmarshalPubkey(pubKeyBytes) if err != nil { - log.Fatalf("decompress pubkey failed, err: %v", err) + logrus.Fatalf("decompress pubkey failed, err: %v", err) } publicKeys = append(publicKeys, pubKey) } if len(os.Args) < 3 { - log.Fatal("the number of arguments is not equal 3") + logrus.Fatal("the number of arguments is not equal 3") } else if len(os.Args) > 3 { keys := 3 publicKeys = make([]*ecdsa.PublicKey, 0) @@ -62,12 +62,12 @@ func main() { IsSMCrypto: false, GroupID: 1, PrivateKey: privateKey, NodeURL: endpoint} c, err := client.Dial(config) if err != nil { - log.Fatalf("init publisher failed, err: %v\n", err) + logrus.Fatalf("init publisher failed, err: %v\n", err) } err = c.PublishPrivateTopic(topic, publicKeys) if err != nil { - log.Fatalf("publish topic failed, err: %v\n", err) + logrus.Fatalf("publish topic failed, err: %v\n", err) } fmt.Println("publish topic success") time.Sleep(3 * time.Second) @@ -75,14 +75,14 @@ func main() { message := "Hi, FISCO BCOS!" go func() { for i := 0; i < 1000; i++ { - log.Printf("publish message: %s ", message+" "+strconv.Itoa(i)) + logrus.Printf("publish message: %s ", message+" "+strconv.Itoa(i)) response, err := c.SendAMOPPrivateMsg(topic, []byte(message+" "+strconv.Itoa(i))) if len(response) != 0 { - log.Printf(" response is: %s ", string(response)) + logrus.Printf(" response is: %s ", string(response)) } time.Sleep(2 * time.Second) if err != nil { - log.Printf("PushAuthTopicDataRandom failed, err: %v\n", err) + logrus.Printf("PushAuthTopicDataRandom failed, err: %v\n", err) } } }() diff --git a/examples/eventLog/sub/subscriber.go b/examples/eventLog/sub/subscriber.go new file mode 100644 index 00000000..9682b9ff --- /dev/null +++ b/examples/eventLog/sub/subscriber.go @@ -0,0 +1,91 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "os" + "os/signal" + "time" + + "github.com/FISCO-BCOS/go-sdk/client" + "github.com/FISCO-BCOS/go-sdk/conf" + "github.com/FISCO-BCOS/go-sdk/core/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/sirupsen/logrus" +) + +func main() { + if len(os.Args) < 2 { + logrus.Fatalf("parameters are not enough, example \n%s 127.0.0.1:20200 hello", os.Args[0]) + } + endpoint := os.Args[1] + privateKey, _ := hex.DecodeString("145e247e170ba3afd6ae97e88f00dbc976c2345d511b0f6713355d19d8b80b58") + config := &conf.Config{IsHTTP: false, ChainID: 1, CAFile: "ca.crt", Key: "sdk.key", Cert: "sdk.crt", + IsSMCrypto: false, GroupID: 1, PrivateKey: privateKey, NodeURL: endpoint} + var c *client.Client + var err error + const ( + indent = " " + ) + for i := 0; i < 3; i++ { + logrus.Printf("%d try to connect\n", i) + c, err = client.Dial(config) + if err != nil { + logrus.Printf("init subscriber failed, err: %v, retrying\n", err) + continue + } + break + } + if err != nil { + logrus.Fatalf("init subscriber failed, err: %v\n", err) + } + var eventLogParams types.EventLogParams + eventLogParams.FromBlock = "1" + eventLogParams.ToBlock = "latest" + eventLogParams.GroupID = "1" + var topics = make([]string, 1) + topics[0] = common.BytesToHash(crypto.Keccak256([]byte("TransferEvent(int256,string,string,uint256)"))).Hex() + eventLogParams.Topics = topics + var addresses = make([]string, 1) + addresses[0] = "0xd2cf82e18f3d2c5cae0de87d29994be622f3fdd3" + eventLogParams.Addresses = addresses + + timeout := 10 * time.Second + queryTicker := time.NewTicker(timeout) + defer queryTicker.Stop() + done := make(chan bool) + err = c.SubscribeEventLogs(eventLogParams, func(status int, logs []types.Log) { + logRes, err := json.MarshalIndent(logs, "", indent) + if err != nil { + fmt.Printf("logs marshalIndent error: %v", err) + } + + logrus.Printf("received: %s\n", logRes) + logrus.Printf("received status: %d\n", status) + queryTicker.Stop() + queryTicker = time.NewTicker(timeout) + done <- true + }) + if err != nil { + logrus.Printf("subscribe event failed, err: %v\n", err) + return + } + + killSignal := make(chan os.Signal, 1) + signal.Notify(killSignal, os.Interrupt) + for { + select { + case <-done: + logrus.Println("Done!") + os.Exit(0) + case <-queryTicker.C: + logrus.Printf("can't receive message after 10s, %s\n", time.Now().String()) + os.Exit(1) + case <-killSignal: + logrus.Println("user exit") + os.Exit(0) + } + } +} diff --git a/examples/kvtable_test_async/main.go b/examples/kvtable_test_async/main.go index b12b2651..6a4b577f 100644 --- a/examples/kvtable_test_async/main.go +++ b/examples/kvtable_test_async/main.go @@ -3,11 +3,11 @@ package main import ( "encoding/hex" "fmt" - "log" "math/big" "strings" "github.com/ethereum/go-ethereum/common" + "github.com/sirupsen/logrus" "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/client" @@ -38,7 +38,7 @@ func invokeSetHandler(receipt *types.Receipt, err error) { } setedLines, err := parseOutput(kvtable.KVTableTestABI, "set", receipt) if err != nil { - log.Fatalf("error when transfer string to int: %v\n", err) + logrus.Fatalf("error when transfer string to int: %v\n", err) } fmt.Printf("seted lines: %v\n", setedLines.Int64()) channel <- 0 @@ -47,7 +47,7 @@ func invokeSetHandler(receipt *types.Receipt, err error) { func main() { configs, err := conf.ParseConfigFile("config.toml") if err != nil { - log.Fatal(err) + logrus.Fatal(err) } config := &configs[0] @@ -55,11 +55,11 @@ func main() { fmt.Println("-------------------starting deploy contract-----------------------") client, err := client.Dial(config) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } tx, err := kvtable.AsyncDeployKVTableTest(client.GetTransactOpts(), deployContractHandler, client) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } fmt.Println("transaction hash: ", tx.Hash().Hex()) <-channel @@ -68,7 +68,7 @@ func main() { fmt.Println("\n-------------------starting invoke Set to insert info-----------------------") instance, err := kvtable.NewKVTableTest(contractAddress, client) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } kvtabletestSession := &kvtable.KVTableTestSession{Contract: instance, CallOpts: *client.GetCallOpts(), TransactOpts: *client.GetTransactOpts()} id := "100010001001" @@ -76,7 +76,7 @@ func main() { item_price := big.NewInt(6000) tx, err = kvtabletestSession.AsyncSet(invokeSetHandler, id, item_price, item_name) // call set API if err != nil { - log.Fatal(err) + logrus.Fatal(err) } fmt.Printf("tx sent: %s\n", tx.Hash().Hex()) <-channel @@ -85,10 +85,10 @@ func main() { fmt.Println("\n-------------------starting invoke Get to query info-----------------------") bool, item_price, item_name, err := kvtabletestSession.Get(id) // call get API if err != nil { - log.Fatal(err) + logrus.Fatal(err) } if !bool { - log.Fatalf("id:%v is not found \n", id) + logrus.Fatalf("id:%v is not found \n", id) } fmt.Printf("id: %v, item_price: %v, item_name: %v \n", id, item_price, item_name) } diff --git a/examples/kvtable_test_sync/main.go b/examples/kvtable_test_sync/main.go index ae1101e9..e46b4e55 100644 --- a/examples/kvtable_test_sync/main.go +++ b/examples/kvtable_test_sync/main.go @@ -3,7 +3,6 @@ package main import ( "encoding/hex" "fmt" - "log" "math/big" "strings" @@ -12,24 +11,25 @@ import ( "github.com/FISCO-BCOS/go-sdk/conf" "github.com/FISCO-BCOS/go-sdk/core/types" kvtable "github.com/FISCO-BCOS/go-sdk/examples" // import kvtabletest + "github.com/sirupsen/logrus" ) func main() { configs, err := conf.ParseConfigFile("config.toml") if err != nil { - log.Fatal(err) + logrus.Fatal(err) } config := &configs[0] client, err := client.Dial(config) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } // deploy contract fmt.Println("-------------------starting deploy contract-----------------------") address, tx, instance, err := kvtable.DeployKVTableTest(client.GetTransactOpts(), client) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } fmt.Println("contract address: ", address.Hex()) // the address should be saved fmt.Println("transaction hash: ", tx.Hash().Hex()) @@ -43,12 +43,12 @@ func main() { item_price := big.NewInt(6000) tx, receipt, err := kvtabletestSession.Set(id, item_price, item_name) // call set API if err != nil { - log.Fatal(err) + logrus.Fatal(err) } fmt.Printf("tx sent: %s\n", tx.Hash().Hex()) setedLines, err := parseOutput(kvtable.KVTableTestABI, "set", receipt) if err != nil { - log.Fatalf("error when transfer string to int: %v\n", err) + logrus.Fatalf("error when transfer string to int: %v\n", err) } fmt.Printf("seted lines: %v\n", setedLines.Int64()) @@ -56,10 +56,10 @@ func main() { fmt.Println("\n-------------------starting invoke Get to query info-----------------------") bool, item_price, item_name, err := kvtabletestSession.Get(id) // call get API if err != nil { - log.Fatal(err) + logrus.Fatal(err) } if !bool { - log.Fatalf("id:%v is not found \n", id) + logrus.Fatalf("id:%v is not found \n", id) } fmt.Printf("id: %v, item_price: %v, item_name: %v \n", id, item_price, item_name) } diff --git a/go.mod b/go.mod index c6f63574..af26a019 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/google/uuid v1.1.1 github.com/holiman/uint256 v1.1.1 // indirect github.com/shirou/gopsutil v3.20.12+incompatible // indirect + github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.6.2 diff --git a/mobile/ios/contract_proxy.go b/mobile/ios/contract_proxy.go index e7214a43..6eafe6a6 100644 --- a/mobile/ios/contract_proxy.go +++ b/mobile/ios/contract_proxy.go @@ -6,15 +6,16 @@ import ( "encoding/json" "errors" "fmt" + "math/big" + "strconv" + "sync/atomic" + "github.com/FISCO-BCOS/go-sdk/client" "github.com/FISCO-BCOS/go-sdk/core/types" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rlp" - "math/big" - "strconv" - "sync/atomic" ) const ( @@ -127,20 +128,6 @@ func (c *ContractProxy) SMCrypto() bool { return c.smCrypto } -// 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. -func (c *ContractProxy) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { - panic("implement me") -} - -// SubscribeFilterLogs creates a background log filtering operation, returning -// a subscription immediately, which can be used to stream the found events. -func (c *ContractProxy) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { - panic("implement me") -} - func (c *ContractProxy) Call(ctx context.Context, groupID int, msg ethereum.CallMsg) ([]byte, error) { var hexBytes hexutil.Bytes var cr *callResult @@ -187,6 +174,11 @@ func (c *ContractProxy) CallContext(ctx context.Context, result interface{}, met } } +// SubscribeEventLogs +func (c *ContractProxy) SubscribeEventLogs(eventLogParams types.EventLogParams, handler func(int, []types.Log)) error { + panic("implement me") +} + func (c *ContractProxy) nextID() json.RawMessage { id := atomic.AddUint32(&c.idCounter, 1) return strconv.AppendUint(nil, uint64(id), 10) diff --git a/precompiled/chaingovernance/chain_governance.go b/precompiled/chaingovernance/chain_governance.go index 2e41d530..d7d3ba6b 100644 --- a/precompiled/chaingovernance/chain_governance.go +++ b/precompiled/chaingovernance/chain_governance.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // ChainGovernanceABI is the input ABI used to generate the binding from. diff --git a/precompiled/cns/cns.go b/precompiled/cns/cns.go index 8d705c92..008a1c94 100644 --- a/precompiled/cns/cns.go +++ b/precompiled/cns/cns.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // CnsABI is the input ABI used to generate the binding from. diff --git a/precompiled/config/system_config.go b/precompiled/config/system_config.go index f332e1cc..7f338701 100644 --- a/precompiled/config/system_config.go +++ b/precompiled/config/system_config.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // ConfigABI is the input ABI used to generate the binding from. diff --git a/precompiled/consensus/consensus.go b/precompiled/consensus/consensus.go index e1b24cc9..9e82d407 100644 --- a/precompiled/consensus/consensus.go +++ b/precompiled/consensus/consensus.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // ConsensusABI is the input ABI used to generate the binding from. diff --git a/precompiled/contractlifecycle/contract_life_cycle.go b/precompiled/contractlifecycle/contract_life_cycle.go index 74812022..acc3f9d0 100644 --- a/precompiled/contractlifecycle/contract_life_cycle.go +++ b/precompiled/contractlifecycle/contract_life_cycle.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // ContractLifeCycleABI is the input ABI used to generate the binding from. diff --git a/precompiled/crud/crud.go b/precompiled/crud/crud.go index d2e48c8e..2ec4213a 100644 --- a/precompiled/crud/crud.go +++ b/precompiled/crud/crud.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // CrudABI is the input ABI used to generate the binding from. diff --git a/precompiled/crud/table_factory.go b/precompiled/crud/table_factory.go index 8aaeabcf..4e6d5d4a 100644 --- a/precompiled/crud/table_factory.go +++ b/precompiled/crud/table_factory.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // TableFactoryABI is the input ABI used to generate the binding from. diff --git a/precompiled/permission/permission.go b/precompiled/permission/permission.go index a5c39769..be36ea83 100644 --- a/precompiled/permission/permission.go +++ b/precompiled/permission/permission.go @@ -10,7 +10,6 @@ import ( "github.com/FISCO-BCOS/go-sdk/abi" "github.com/FISCO-BCOS/go-sdk/abi/bind" "github.com/FISCO-BCOS/go-sdk/core/types" - "github.com/FISCO-BCOS/go-sdk/event" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" ) @@ -24,7 +23,6 @@ var ( _ = bind.Bind _ = common.Big1 _ = types.BloomLookup - _ = event.NewSubscription ) // PermissionABI is the input ABI used to generate the binding from. diff --git a/smcrypto/sm2.go b/smcrypto/sm2.go index ea0f30bd..7a1e5174 100644 --- a/smcrypto/sm2.go +++ b/smcrypto/sm2.go @@ -4,12 +4,12 @@ import ( "bytes" "crypto/rand" "encoding/binary" - "log" "math/big" "github.com/FISCO-BCOS/crypto/ecdsa" "github.com/FISCO-BCOS/crypto/elliptic" "github.com/FISCO-BCOS/go-sdk/smcrypto/sm3" + "github.com/sirupsen/logrus" ) const defaultSM2ID = "1234567812345678" @@ -21,35 +21,35 @@ func SM2PreProcess(src []byte, id string, priv *ecdsa.PrivateKey) ([]byte, error buf := bytes.NewBuffer(data) err := binary.Write(buf, binary.BigEndian, length) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } _, err = buf.Write([]byte(id)) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } _, err = buf.Write(elliptic.Sm2p256v1().Params().A.Bytes()) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } _, err = buf.Write(elliptic.Sm2p256v1().Params().B.Bytes()) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } _, err = buf.Write(elliptic.Sm2p256v1().Params().Gx.Bytes()) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } _, err = buf.Write(elliptic.Sm2p256v1().Params().Gy.Bytes()) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } _, err = buf.Write(priv.X.Bytes()) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } _, err = buf.Write(priv.Y.Bytes()) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } z := sm3.Hash(buf.Bytes()) // fmt.Printf("digest sm3 hash :%x\n", z) diff --git a/smcrypto/sm3/sm3.go b/smcrypto/sm3/sm3.go index b5d767e7..91c16217 100644 --- a/smcrypto/sm3/sm3.go +++ b/smcrypto/sm3/sm3.go @@ -3,8 +3,9 @@ package sm3 import ( "bytes" "encoding/binary" - "log" "math/big" + + "github.com/sirupsen/logrus" ) // var iv = []uint32{0x7380166f, 0x4914b2b9, 0x172442d7, @@ -64,31 +65,31 @@ func (sm3 *Context) cf(v, b []byte) []byte { buf := bytes.NewBuffer(v) err := binary.Read(buf, binary.BigEndian, &sm3.a) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Read(buf, binary.BigEndian, &sm3.b) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Read(buf, binary.BigEndian, &sm3.c) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Read(buf, binary.BigEndian, &sm3.d) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Read(buf, binary.BigEndian, &sm3.e) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Read(buf, binary.BigEndian, &sm3.f) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Read(buf, binary.BigEndian, &sm3.g) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } binary.Read(buf, binary.BigEndian, &sm3.h) @@ -130,35 +131,35 @@ func (sm3 *Context) cf(v, b []byte) []byte { retBuf := bytes.NewBuffer(ret) err = binary.Write(retBuf, binary.BigEndian, sm3.a^A) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Write(retBuf, binary.BigEndian, sm3.b^B) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Write(retBuf, binary.BigEndian, sm3.c^C) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Write(retBuf, binary.BigEndian, sm3.d^D) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Write(retBuf, binary.BigEndian, sm3.e^E) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Write(retBuf, binary.BigEndian, sm3.f^F) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Write(retBuf, binary.BigEndian, sm3.g^G) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } err = binary.Write(retBuf, binary.BigEndian, sm3.h^H) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } return retBuf.Bytes() @@ -177,7 +178,7 @@ func splitB(b []byte) (w68 [68]uint32, w64 [64]uint32) { for i := 0; i < 16; i++ { err := binary.Read(buf, binary.BigEndian, &w68[i]) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } } for i := 16; i < 68; i++ { @@ -198,13 +199,13 @@ func pad(src []byte) []byte { buf := bytes.NewBuffer(src) err := buf.WriteByte(paddingHeader) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } padLength -= 8 for padLength > 0 { err = buf.WriteByte(0) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } padLength -= 8 } diff --git a/smcrypto/sm_crypto.go b/smcrypto/sm_crypto.go index 15679a01..39cc8b03 100644 --- a/smcrypto/sm_crypto.go +++ b/smcrypto/sm_crypto.go @@ -5,7 +5,6 @@ import ( "encoding/pem" "errors" "fmt" - "log" "math/big" "github.com/FISCO-BCOS/crypto/ecdsa" @@ -13,6 +12,7 @@ import ( "github.com/FISCO-BCOS/crypto/x509" "github.com/FISCO-BCOS/go-sdk/smcrypto/sm3" "github.com/ethereum/go-ethereum/common" + "github.com/sirupsen/logrus" ) const publicKeyLength = 64 @@ -141,7 +141,7 @@ func Sign(hash, privateKey []byte) (sig []byte, err error) { r, s, err := SM2Sign(hash, key) if err != nil { - log.Fatal(err) + logrus.Fatal(err) } sig = make([]byte, 128) copy(sig[32-len(r.Bytes()):], r.Bytes())