Create a New Sidechain
When creating a new sidechain, all RPC interfaces should be implemented according to RPC interface specification, and the SPV module should be integrated for block verification. Take DID as an example to illustrate the specific initiating process:

1 . Instance of Initializing the Sidechain Block Storage

Declare the file location of block storage and initialize the block according to the genesis parameters:
1
ChainStore, err := bc.NewChainStore(activeNetParams.GenesisBlock,
2
filepath.Join(DataPath, DataDir, ChainDir))
Copied!

2. Initialize the SPV Module and Trading Pits

The SPV module is mainly used to synchronize transactions with the mainchain and scan transactions subscribed by the mainchain for processing:
1
spvCfg := spv.Config{
2
DataDir: filepath.Join(DataPath, DataDir, SpvDir),
3
ChainParams: spvNetParams,
4
PermanentPeers: cfg.SPVPermanentPeers,
5
GenesisAddress: genesisAddress,
6
FilterType: filter.FTCustomID,
7
NodeVersion: nodePrefix + Version,
8
}
9
spvService, err := spv.NewService(&spvCfg)
Copied!

3. Start the P2P Network

Data between sidechain nodes is synchronized through the P2P network:
1
server, err := server.New(&server.Config{
2
DataDir: filepath.Join(DataPath, DataDir),
3
Chain: chain,
4
TxMemPool: txPool,
5
ChainParams: activeNetParams,
6
PermanentPeers: cfg.PermanentPeers,
7
NodeVersion: nodePrefix + Version,
8
})
Copied!

4. SubmitAuxBlockWithBlock

The following is an example of the PoW consensus. SubmitAuxBlockWithBlock is how the sidechain can import the HashRate of the mainchain without HashRate support:
1
powService := pow.NewService(&powCfg)
2
go powService.Start()
3
spvBlockListener.RegisterFunc(powService.SubmitAuxBlockWithBlock)
Copied!
PoS consensus is recommended.

5. Start RPC and Other Sidechain Services

1
httpService := sv.NewHttpService(&serviceCfg)
2
rpcServer := newRPCServer(cfg.RPCPort, httpService)
Copied!

6. The Sidechain Interface needs to Implement the RPC Core Interface

Take some interfaces that implement Golang language as examples.
getblock: query block information through hash
1
func (s *HttpService) GetBlockByHash(param http.Params) (interface{}, error) {
2
str, ok := param.String("blockhash")
3
if !ok {
4
return nil, http.NewError(int(InvalidParams), "block hash not found")
5
}
6
7
var hash common.Uint256
8
hashBytes, err := FromReversedString(str)
9
if err != nil {
10
return nil, http.NewError(int(InvalidParams), "invalid block hash")
11
}
12
if err := hash.Deserialize(bytes.NewReader(hashBytes)); err != nil {
13
return nil, http.NewError(int(InvalidParams), "invalid block hash")
14
}
15
16
verbosity, ok := param.Uint("verbosity")
17
if !ok {
18
verbosity = 1
19
}
20
21
return s.getBlock(hash, verbosity)
22
}
Copied!
getcurrentheight: get the block height
1
func (b *BlockChain) GetBestHeight() uint32 {
2
return b.db.GetHeight()
3
}
Copied!
getblockhash: get block hash through height
1
func (s *HttpService) GetBlockHash(param http.Params) (interface{}, error) {
2
height, ok := param.Uint32("height")
3
if !ok {
4
return nil, http.NewError(int(InvalidParams), " height parameter should be a positive integer")
5
}
6
7
hash, err := s.cfg.Chain.GetBlockHash(height)
8
if err != nil {
9
return nil, newError(InvalidParams)
10
}
11
return ToReversedString(hash), nil
12
}
Copied!
getrawmempool: get memory pool transaction
1
func (s *HttpService) GetTransactionPool(param http.Params) (interface{}, error) {
2
txs := make([]*TransactionInfo, 0)
3
for _, t := range s.cfg.TxMemPool.GetTxsInPool() {
4
txs = append(txs, s.cfg.GetTransactionInfo(s.cfg, nil, t))
5
}
6
return txs, nil
7
}
Copied!
getrawtransaction: get transaction information by transaction ID
1
func (s *HttpService) GetRawTransaction(param http.Params) (interface{}, error) {
2
str, ok := param.String("txid")
3
if !ok {
4
return nil, newError(InvalidParams)
5
}
6
7
hex, err := FromReversedString(str)
8
if err != nil {
9
return nil, newError(InvalidParams)
10
}
11
var hash common.Uint256
12
err = hash.Deserialize(bytes.NewReader(hex))
13
if err != nil {
14
return nil, newError(InvalidParams)
15
}
16
var header interfaces.Header
17
tx, height, err := s.cfg.Chain.GetTransaction(hash)
18
if err != nil {
19
//try to find transaction in transaction pool.
20
tx = s.cfg.TxMemPool.GetTransaction(hash)
21
if tx == nil {
22
return nil, newError(UnknownTransaction)
23
}
24
} else {
25
bHash, err := s.cfg.Chain.GetBlockHash(height)
26
if err != nil {
27
return nil, newError(UnknownTransaction)
28
}
29
header, err = s.cfg.Chain.GetHeader(bHash)
30
if err != nil {
31
return nil, newError(UnknownTransaction)
32
}
33
}
34
35
verbose, ok := param.Bool("verbose")
36
if verbose {
37
return s.cfg.GetTransactionInfo(s.cfg, header, tx), nil
38
} else {
39
buf := new(bytes.Buffer)
40
tx.Serialize(buf)
41
return common.BytesToHexString(buf.Bytes()), nil
42
}
43
}
Copied!
getblockcount: get the next block height
1
func (s *HttpService) GetBlockCount(param http.Params) (interface{}, error) {
2
return s.cfg.Chain.GetBestHeight() + 1, nil
3
}
Copied!
getblockbyheight: get blocks by height
1
func (s *HttpService) GetBlockByHeight(param http.Params) (interface{}, error) {
2
height, ok := param.Uint("height")
3
if !ok {
4
return nil, http.NewError(int(InvalidParams), "height parameter should be a positive integer")
5
}
6
7
hash, err := s.cfg.Chain.GetBlockHash(uint32(height))
8
if err != nil {
9
return nil, newError(UnknownBlock)
10
}
11
12
return s.getBlock(hash, 2)
13
}
Copied!
getbestblockhash: get the highest block hash
1
func (s *HttpService) GetBestBlockHash(param http.Params) (interface{}, error) {
2
height := s.cfg.Chain.GetBestHeight()
3
hash, err := s.cfg.Chain.GetBlockHash(height)
4
if err != nil {
5
return nil, newError(InvalidParams)
6
}
7
return ToReversedString(hash), nil
8
}
Copied!
sendrechargetransaction: send recharge transaction
1
func (s *HttpService) SendRechargeToSideChainTxByHash(param http.Params) (interface{}, error) {
2
if ok := CheckRPCServiceLevel(s.cfg.ConfigurationPermitted, config.TransactionPermitted); ok != nil {
3
return nil, http.NewError(int(InvalidMethod), "requesting method if out of service level")
4
}
5
6
txid, ok := param.String("txid")
7
if !ok {
8
return nil, http.NewError(int(InvalidParams), "txid not found")
9
}
10
11
txBytes, err := common.HexStringToBytes(txid)
12
if err != nil {
13
return nil, http.NewError(int(InvalidParams), "invalid txid")
14
}
15
16
hash, err := common.Uint256FromBytes(txBytes)
17
if err != nil {
18
return nil, http.NewError(int(InvalidParams), "to tx hash failed")
19
}
20
21
tx, err := s.cfg.SpvService.GetTransaction(hash)
22
if err != nil {
23
return nil, http.NewError(int(InvalidParams), "invalid tx hash")
24
}
25
26
depositTx, err := createRechargeToSideChainTransaction(tx, s.cfg.GenesisAddress)
27
if err != nil {
28
return nil, http.NewError(int(InvalidParams), "create recharge tx failed")
29
}
30
31
if err := s.verifyAndSendTx(depositTx); err != nil {
32
return nil, ruleError(err)
33
}
34
return depositTx.Hash().String(), nil
35
}
Copied!
sendrawtransaction: send transaction
1
func (s *HttpService) SendRawTransaction(param http.Params) (interface{}, error) {
2
if ok := CheckRPCServiceLevel(s.cfg.ConfigurationPermitted, config.TransactionPermitted); ok != nil {
3
return nil, http.NewError(int(InvalidMethod), "requesting method if out of service level")
4
}
5
6
str, ok := param.String("data")
7
if !ok {
8
return nil, http.NewError(int(InvalidParams), "need a string parameter named data")
9
}
10
11
bys, err := common.HexStringToBytes(str)
12
if err != nil {
13
return nil, http.NewError(int(InvalidParams), "hex string to bytes error:"+err.Error())
14
}
15
var txn types.Transaction
16
if err := txn.Deserialize(bytes.NewReader(bys)); err != nil {
17
return nil, http.NewError(int(InvalidTransaction), "transaction deserialize error:"+err.Error())
18
}
19
20
if err := s.verifyAndSendTx(&txn); err != nil {
21
return nil, ruleError(err)
22
}
23
24
return ToReversedString(txn.Hash()), nil
25
}
Copied!
getwithdrawtransaction: get withdrawal transaction
1
func (s *HttpService) GetWithdrawTransactionByHash(param http.Params) (interface{}, error) {
2
str, ok := param.String("txid")
3
if !ok {
4
return nil, http.NewError(int(InvalidParams), "txid not found")
5
}
6
hex, err := FromReversedString(str)
7
if err != nil {
8
return nil, http.NewError(int(InvalidParams), "txid reverse failed")
9
}
10
var hash common.Uint256
11
err = hash.Deserialize(bytes.NewReader(hex))
12
if err != nil {
13
return nil, http.NewError(int(InvalidTransaction), "txid deserialize failed")
14
}
15
tx, _, err := s.cfg.Chain.GetTransaction(hash)
16
if err != nil {
17
return nil, http.NewError(int(UnknownTransaction), "get tx by txid failed")
18
}
19
payload, ok := tx.Payload.(*types.PayloadTransferCrossChainAsset)
20
if !ok {
21
return nil, http.NewError(int(UnknownTransaction), "get tx by txid failed")
22
}
23
24
var txOuputsInfo []*WithdrawOutputInfo
25
for i := 0; i < len(payload.CrossChainAmounts); i++ {
26
txOuputsInfo = append(txOuputsInfo, &WithdrawOutputInfo{
27
CrossChainAddress: payload.CrossChainAddresses[i],
28
CrossChainAmount: payload.CrossChainAmounts[i].String(),
29
OutputAmount: tx.Outputs[payload.OutputIndexes[i]].Value.String(),
30
})
31
}
32
33
txWithdraw := WithdrawTxInfo{
34
TxID: ToReversedString(tx.Hash()),
35
CrossChainAssets: txOuputsInfo,
36
}
37
38
return txWithdraw, nil
39
}
Copied!
getwithdrawtransactionsbyweight: get the withdrawal transaction at a certain height
1
func (s *HttpService) GetWithdrawTransactionsByHeight(param http.Params) (interface{}, error) {
2
height, ok := param.Uint("height")
3
if !ok {
4
return nil, http.NewError(int(InvalidParams), "height parameter should be a positive integer")
5
}
6
7
hash, err := s.cfg.Chain.GetBlockHash(uint32(height))
8
if err != nil {
9
return nil, newError(UnknownBlock)
10
11
}
12
block, err := s.cfg.Chain.GetBlockByHash(hash)
13
if err != nil {
14
return nil, newError(UnknownBlock)
15
}
16
17
destroyHash := common.Uint168{}
18
txs := s.GetBlockTransactionsDetail(block, func(tran *types.Transaction) bool {
19
_, ok := tran.Payload.(*types.PayloadTransferCrossChainAsset)
20
if !ok {
21
return false
22
}
23
for _, output := range tran.Outputs {
24
if output.ProgramHash == destroyHash {
25
return true
26
}
27
}
28
return false
29
})
30
return s.GetWithdrawTxsInfo(txs), nil
31
}
Copied!
getexistdeposittransactions: get the existing recharge transaction
1
func (s *HttpService) GetExistDepositTransactions(param http.Params) (interface{}, error) {
2
txs, ok := GetStringArray(param, "txs")
3
if !ok {
4
return nil, http.NewError(int(InvalidParams), "txs not found")
5
}
6
7
var resultTxHashes []string
8
for _, txHash := range txs {
9
txHashBytes, err := common.HexStringToBytes(txHash)
10
if err != nil {
11
return nil, newError(InvalidParams)
12
}
13
hash, err := common.Uint256FromBytes(txHashBytes)
14
if err != nil {
15
return nil, newError(InvalidParams)
16
}
17
inStore := s.cfg.Chain.IsDuplicateMainchainTx(*hash)
18
inTxPool := s.cfg.TxMemPool.IsDuplicateMainChainTx(*hash)
19
if inTxPool || inStore {
20
resultTxHashes = append(resultTxHashes, txHash)
21
}
22
}
23
24
return resultTxHashes, nil
25
}
Copied!
getillegalevidencebyheight: get the evidence of illegal actions at a certain height
1
func (s *HttpService) GetIllegalEvidenceByHeight(param http.Params) (interface{}, error) {
2
height, ok := param.Uint("height")
3
if !ok {
4
return nil, http.NewError(int(InvalidParams), "height parameter should be a positive integer")
5
}
6
7
hash, err := s.cfg.Chain.GetBlockHash(uint32(height))
8
if err != nil {
9
return nil, newError(UnknownBlock)
10
11
}
12
_, err = s.cfg.Chain.GetBlockByHash(hash)
13
if err != nil {
14
return nil, newError(UnknownBlock)
15
}
16
17
// todo get illegal evidences in block
18
19
result := make([]*SidechainIllegalDataInfo, 0)
20
return result, nil
21
}
Copied!
checkillegalevidence: check evidence of illegal actions
1
func (s *HttpService) CheckIllegalEvidence(param http.Params) (interface{}, error) {
2
evidence, ok := param["evidence"]
3
if !ok {
4
return nil, http.NewError(int(InvalidParams), "no evidence")
5
}
6
e := new(SidechainIllegalDataInfo)
7
if err := Unmarshal(evidence, e); err != nil {
8
log.Error("[CheckIllegalEvidence] received invalid evidence")
9
return false, err
10
}
11
12
// todo check illegal evidence
13
14
return false, nil
15
}
Copied!
Copy link
Contents