mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 03:41:34 +00:00
Merge pull request #724 from nspcc-dev/feature/submitblock
rpc: implement submitblock
This commit is contained in:
commit
f8eee778f4
7 changed files with 129 additions and 23 deletions
|
@ -63,7 +63,7 @@ which would yield the response:
|
|||
| `invokefunction` | Yes |
|
||||
| `invokescript` | Yes |
|
||||
| `sendrawtransaction` | Yes |
|
||||
| `submitblock` | No (#344) |
|
||||
| `submitblock` | Yes |
|
||||
| `validateaddress` | Yes |
|
||||
|
||||
### Unsupported methods
|
||||
|
|
|
@ -52,6 +52,9 @@ var (
|
|||
// ErrPolicy is returned on attempt to add transaction that doesn't
|
||||
// comply with node's configured policy into the mempool.
|
||||
ErrPolicy = errors.New("not allowed by policy")
|
||||
// ErrInvalidBlockIndex is returned when trying to add block with index
|
||||
// other than expected height of the blockchain.
|
||||
ErrInvalidBlockIndex error = errors.New("invalid block index")
|
||||
)
|
||||
var (
|
||||
genAmount = []int{8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||
|
@ -307,7 +310,7 @@ func (bc *Blockchain) AddBlock(block *block.Block) error {
|
|||
|
||||
expectedHeight := bc.BlockHeight() + 1
|
||||
if expectedHeight != block.Index {
|
||||
return fmt.Errorf("expected block %d, but passed block %d", expectedHeight, block.Index)
|
||||
return ErrInvalidBlockIndex
|
||||
}
|
||||
|
||||
headerLen := bc.headerListLen()
|
||||
|
|
|
@ -391,6 +391,17 @@ func TestCreateBasicChain(t *testing.T) {
|
|||
require.NoError(t, writer.Err)
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks for `submitblock` test. If you are planning to modify test chain from `testblocks.acc`,
|
||||
// please, update params value of `empty block` and `positive` tests.
|
||||
var blocks []*block.Block
|
||||
blocks = append(blocks, bc.newBlock(), bc.newBlock(newMinerTX()))
|
||||
for _, b := range blocks {
|
||||
buf := io.NewBufBinWriter()
|
||||
b.EncodeBinary(buf.BinWriter)
|
||||
require.NoError(t, buf.Err)
|
||||
t.Log(hex.EncodeToString(buf.Bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
func newNEP5Transfer(sc, from, to util.Uint160, amount int64) *transaction.Transaction {
|
||||
|
|
|
@ -20,6 +20,18 @@ type (
|
|||
var (
|
||||
// ErrInvalidParams represents a generic 'invalid parameters' error.
|
||||
ErrInvalidParams = NewInvalidParamsError("", nil)
|
||||
// ErrAlreadyExists represents SubmitError with code -501
|
||||
ErrAlreadyExists = NewSubmitError(-501, "Block or transaction already exists and cannot be sent repeatedly.")
|
||||
// ErrOutOfMemory represents SubmitError with code -502
|
||||
ErrOutOfMemory = NewSubmitError(-502, "The memory pool is full and no more transactions can be sent.")
|
||||
// ErrUnableToVerify represents SubmitError with code -503
|
||||
ErrUnableToVerify = NewSubmitError(-503, "The block cannot be validated.")
|
||||
// ErrValidationFailed represents SubmitError with code -504
|
||||
ErrValidationFailed = NewSubmitError(-504, "Block or transaction validation failed.")
|
||||
// ErrPolicyFail represents SubmitError with code -505
|
||||
ErrPolicyFail = NewSubmitError(-505, "One of the Policy filters failed.")
|
||||
// ErrUnknown represents SubmitError with code -500
|
||||
ErrUnknown = NewSubmitError(-500, "Unknown error.")
|
||||
)
|
||||
|
||||
// NewError is an Error constructor that takes Error contents from its
|
||||
|
@ -71,6 +83,12 @@ func NewRPCError(message string, data string, cause error) *Error {
|
|||
return NewError(-100, http.StatusUnprocessableEntity, message, data, cause)
|
||||
}
|
||||
|
||||
// NewSubmitError creates a new error with
|
||||
// specified error code and error message
|
||||
func NewSubmitError(code int64, message string) *Error {
|
||||
return NewError(code, http.StatusUnprocessableEntity, message, "", nil)
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("%s (%d) - %s - %s", e.Message, e.Code, e.Data, e.Cause)
|
||||
|
|
|
@ -187,6 +187,14 @@ var (
|
|||
},
|
||||
)
|
||||
|
||||
submitblockCalled = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Help: "Number of calls to submitblock rpc endpoint",
|
||||
Name: "submitblock_called",
|
||||
Namespace: "neogo",
|
||||
},
|
||||
)
|
||||
|
||||
getstorageCalled = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Help: "Number of calls to getstorage rpc endpoint",
|
||||
|
@ -218,6 +226,7 @@ func init() {
|
|||
gettxoutCalled,
|
||||
getrawtransactionCalled,
|
||||
sendrawtransactionCalled,
|
||||
submitblockCalled,
|
||||
getstorageCalled,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/neo-go/config"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
|
@ -306,6 +307,10 @@ Methods:
|
|||
case "invokescript":
|
||||
results, resultsErr = s.invokescript(reqParams)
|
||||
|
||||
case "submitblock":
|
||||
submitblockCalled.Inc()
|
||||
results, resultsErr = s.submitBlock(reqParams)
|
||||
|
||||
case "sendrawtransaction":
|
||||
sendrawtransactionCalled.Inc()
|
||||
results, resultsErr = s.sendrawtransaction(reqParams)
|
||||
|
@ -831,6 +836,34 @@ func (s *Server) runScriptInVM(script []byte) *result.Invoke {
|
|||
return result
|
||||
}
|
||||
|
||||
// submitBlock broadcasts a raw block over the NEO network.
|
||||
func (s *Server) submitBlock(reqParams request.Params) (interface{}, error) {
|
||||
param, ok := reqParams.ValueWithType(0, request.StringT)
|
||||
if !ok {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
blockBytes, err := param.GetBytesHex()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
b := block.Block{}
|
||||
r := io.NewBinReaderFromBuf(blockBytes)
|
||||
b.DecodeBinary(r)
|
||||
if r.Err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
err = s.chain.AddBlock(&b)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case core.ErrInvalidBlockIndex, core.ErrAlreadyExists:
|
||||
return nil, response.ErrAlreadyExists
|
||||
default:
|
||||
return nil, response.ErrValidationFailed
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *Server) sendrawtransaction(reqParams request.Params) (interface{}, error) {
|
||||
var resultsErr error
|
||||
var results interface{}
|
||||
|
@ -844,28 +877,24 @@ func (s *Server) sendrawtransaction(reqParams request.Params) (interface{}, erro
|
|||
tx := &transaction.Transaction{}
|
||||
tx.DecodeBinary(r)
|
||||
if r.Err != nil {
|
||||
err = errors.Wrap(r.Err, "transaction DecodeBinary failed")
|
||||
} else {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
relayReason := s.coreServer.RelayTxn(tx)
|
||||
switch relayReason {
|
||||
case network.RelaySucceed:
|
||||
results = true
|
||||
case network.RelayAlreadyExists:
|
||||
err = errors.New("block or transaction already exists and cannot be sent repeatedly")
|
||||
resultsErr = response.ErrAlreadyExists
|
||||
case network.RelayOutOfMemory:
|
||||
err = errors.New("the memory pool is full and no more transactions can be sent")
|
||||
resultsErr = response.ErrOutOfMemory
|
||||
case network.RelayUnableToVerify:
|
||||
err = errors.New("the block cannot be validated")
|
||||
resultsErr = response.ErrUnableToVerify
|
||||
case network.RelayInvalid:
|
||||
err = errors.New("block or transaction validation failed")
|
||||
resultsErr = response.ErrValidationFailed
|
||||
case network.RelayPolicyFail:
|
||||
err = errors.New("one of the Policy filters failed")
|
||||
resultsErr = response.ErrPolicyFail
|
||||
default:
|
||||
err = errors.New("unknown error")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
resultsErr = response.NewInternalServerError(err.Error(), err)
|
||||
resultsErr = response.ErrUnknown
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -767,6 +767,42 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
fail: true,
|
||||
},
|
||||
},
|
||||
"submitblock": {
|
||||
{
|
||||
name: "empty block",
|
||||
params: `["00000000270dd14a8ddb4961cada75e9ec4cce906f9feeaef21b1d6cea5b2f0230baf92e00000000000000000000000000000000000000000000000000000000000000009f0e625ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd040140bd2b961ca7df75d6d8896fd4ef487128ddd1ca9feda9cb09da58a6a24607fc71cd572ba072e4ec9d9847c7da6a33fc44dc9a64208fd62851d27d330012bd0bf640ee6e92653789b0d1c87e0cd485c5bbd84194479995930567df9ffec7ab370c965df320134632e53d139a785840cecb16c4fe801f3c6bed17a9dd83460f5f297440fbe5f9d3cc23f9c7b9f10e191eae541fcb71eb03799c09886aa04c0bc43e98c2c91686a5b4fb988c8855f4d217807f01ca9a61bc0d5536eb3149dce7ae66965c407af69085454fb39f0c27cf308219c09efbe902f0a092a065b38864c97529f1dd12fa54c346e8d9482106c7154b3170c150887170c78fe78a5c70e3956880489c8b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae00"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid block height",
|
||||
params: `["000000005fb86f62eafe8e9246bc0d1648e4e5c8389dee9fb7fe03fcc6772ec8c5e4ec2aedb908054ac1409be5f77d5369c6e03490b2f6676d68d0b3370f8159e0fdadf99bc05f5e030000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401406f299c82b513f59f5bd120317974852c9694c6e10db1ef2f1bb848b1a33e47a08f8dc03ee784166b2060a94cd4e7af88899b39787938f7f2763ea4d2182776ed40f3bafd85214fef38a4836ca97793001ea411f553c51e88781f7b916c59c145bff28314b6e7ea246789422a996fc4937e290a1b40f6b97c5222540f65b0d47aca40d2b3d19203d456428bfdb529e846285052105957385b65388b9a617f6e2d56a64ec41aa73439eafccb52987bb1975c9b67518b053d9e61b445e4a3377dbc206640bd688489bd62adf6bed9d61a73905b9591eb87053c6f0f4dd70f3bee7295541b490caef044b55b6f9f01dc4a05a756a3f2edd06f5adcbe4e984c1e552f9023f08b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae0100000000000000000000"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid hex",
|
||||
params: `["000000005gb86f62eafe8e9246bc0d1648e4e5c8389dee9fb7fe03fcc6772ec8c5e4ec2aedb908054ac1409be5f77d5369c6e03490b2f6676d68d0b3370f8159e0fdadf99bc05f5e030000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401406f299c82b513f59f5bd120317974852c9694c6e10db1ef2f1bb848b1a33e47a08f8dc03ee784166b2060a94cd4e7af88899b39787938f7f2763ea4d2182776ed40f3bafd85214fef38a4836ca97793001ea411f553c51e88781f7b916c59c145bff28314b6e7ea246789422a996fc4937e290a1b40f6b97c5222540f65b0d47aca40d2b3d19203d456428bfdb529e846285052105957385b65388b9a617f6e2d56a64ec41aa73439eafccb52987bb1975c9b67518b053d9e61b445e4a3377dbc206640bd688489bd62adf6bed9d61a73905b9591eb87053c6f0f4dd70f3bee7295541b490caef044b55b6f9f01dc4a05a756a3f2edd06f5adcbe4e984c1e552f9023f08b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae0100000000000000000000"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid block bytes",
|
||||
params: `["0000000027"]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "positive",
|
||||
// If you are planning to modify test chain from `testblocks.acc`, please, update param value
|
||||
params: `["00000000270dd14a8ddb4961cada75e9ec4cce906f9feeaef21b1d6cea5b2f0230baf92eedb908054ac1409be5f77d5369c6e03490b2f6676d68d0b3370f8159e0fdadf9b93a615ed10000005704000000000000be48d3a3f5d10013ab9ffee489706078714f1ea201fd0401402dca64bfc20107819171fab7c636427e9634a58f560ff9ef94ff9005a82509dd5e1b054d195201013a8477529d023d794a3ca6efa717f5d7e541e3b81fbabff840b5395429f0da5a8d1023907dca3771c02eca31e520e1acc2851b373a6aedd5bf6ba75a80b0949ee0794156ae48356b98e761143b90ebaa816ca4b0b0f91b38ab40a6feb77e09c0c95c6f2c6571155843f388e1f7cb7ba5af2bc235bac1d57a8c316d817e54895bfdb98b9c34ed9550e7ccff2a06b46ca0f2b99ba720368d9934eb40dbbdc94154cc304296ccda73e4e0257cdc344c4bad4c70f419f59f5d6d862819cead91c55e73e7c30daff2cd38d74eae3e0e49a5226de3060843d0e3b9262dc08b532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae0100000000000000000000"]`,
|
||||
result: func(e *executor) interface{} {
|
||||
v := true
|
||||
return &v
|
||||
},
|
||||
},
|
||||
},
|
||||
"validateaddress": {
|
||||
{
|
||||
name: "positive",
|
||||
|
|
Loading…
Reference in a new issue