208 lines
5.0 KiB
Go
208 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
|
)
|
|
|
|
// BridgeChaincode provides functions for cross-chain asset bridging
|
|
type BridgeChaincode struct {
|
|
contractapi.Contract
|
|
}
|
|
|
|
// Asset represents a bridged asset
|
|
type Asset struct {
|
|
ID string `json:"id"`
|
|
Owner string `json:"owner"`
|
|
Amount uint64 `json:"amount"`
|
|
TokenType string `json:"tokenType"`
|
|
Locked bool `json:"locked"`
|
|
EvmTxHash string `json:"evmTxHash,omitempty"`
|
|
RequestID string `json:"requestId,omitempty"`
|
|
}
|
|
|
|
// InitLedger initializes the ledger with sample assets
|
|
func (s *BridgeChaincode) InitLedger(ctx contractapi.TransactionContextInterface) error {
|
|
assets := []Asset{
|
|
{ID: "asset1", Owner: "Org1MSP", Amount: 1000, TokenType: "ERC20", Locked: false},
|
|
{ID: "asset2", Owner: "Org2MSP", Amount: 2000, TokenType: "ERC20", Locked: false},
|
|
}
|
|
|
|
for _, asset := range assets {
|
|
assetJSON, err := json.Marshal(asset)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = ctx.GetStub().PutState(asset.ID, assetJSON)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to put to world state. %v", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ReadAsset returns the asset stored in the world state with given id
|
|
func (s *BridgeChaincode) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
|
|
assetJSON, err := ctx.GetStub().GetState(id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read from world state: %v", err)
|
|
}
|
|
if assetJSON == nil {
|
|
return nil, fmt.Errorf("the asset %s does not exist", id)
|
|
}
|
|
|
|
var asset Asset
|
|
err = json.Unmarshal(assetJSON, &asset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &asset, nil
|
|
}
|
|
|
|
// LockAsset locks an asset for bridge transfer to EVM
|
|
func (s *BridgeChaincode) LockAsset(
|
|
ctx contractapi.TransactionContextInterface,
|
|
assetId string,
|
|
evmDestination string,
|
|
requestId string,
|
|
) error {
|
|
asset, err := s.ReadAsset(ctx, assetId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if asset.Locked {
|
|
return fmt.Errorf("asset %s is already locked", assetId)
|
|
}
|
|
|
|
asset.Locked = true
|
|
asset.RequestID = requestId
|
|
assetJSON, err := json.Marshal(asset)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Emit event for EVM bridge to pick up
|
|
err = ctx.GetStub().SetEvent("BridgeToEVM", []byte(fmt.Sprintf(`{"assetId":"%s","amount":%d,"evmRecipient":"%s","requestId":"%s"}`, assetId, asset.Amount, evmDestination, requestId)))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set event: %v", err)
|
|
}
|
|
|
|
return ctx.GetStub().PutState(assetId, assetJSON)
|
|
}
|
|
|
|
// UnlockAsset unlocks an asset after bridge completion
|
|
func (s *BridgeChaincode) UnlockAsset(
|
|
ctx contractapi.TransactionContextInterface,
|
|
assetId string,
|
|
evmTxHash string,
|
|
) error {
|
|
asset, err := s.ReadAsset(ctx, assetId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
asset.Locked = false
|
|
asset.EvmTxHash = evmTxHash
|
|
assetJSON, err := json.Marshal(asset)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ctx.GetStub().PutState(assetId, assetJSON)
|
|
}
|
|
|
|
// BurnAsset burns an asset on Fabric side (mint on EVM side)
|
|
func (s *BridgeChaincode) BurnAsset(
|
|
ctx contractapi.TransactionContextInterface,
|
|
assetId string,
|
|
evmRecipient string,
|
|
requestId string,
|
|
) error {
|
|
asset, err := s.ReadAsset(ctx, assetId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Emit event for EVM minting
|
|
err = ctx.GetStub().SetEvent("BridgeToEVM", []byte(fmt.Sprintf(`{"assetId":"%s","amount":%d,"evmRecipient":"%s","requestId":"%s","action":"mint"}`, assetId, asset.Amount, evmRecipient, requestId)))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set event: %v", err)
|
|
}
|
|
|
|
// Delete asset (burn)
|
|
return ctx.GetStub().DelState(assetId)
|
|
}
|
|
|
|
// CreateAsset creates a new asset
|
|
func (s *BridgeChaincode) CreateAsset(
|
|
ctx contractapi.TransactionContextInterface,
|
|
id string,
|
|
amount uint64,
|
|
tokenType string,
|
|
) error {
|
|
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get client MSP ID: %v", err)
|
|
}
|
|
|
|
asset := Asset{
|
|
ID: id,
|
|
Owner: clientMSPID,
|
|
Amount: amount,
|
|
TokenType: tokenType,
|
|
Locked: false,
|
|
}
|
|
|
|
assetJSON, err := json.Marshal(asset)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return ctx.GetStub().PutState(id, assetJSON)
|
|
}
|
|
|
|
// GetAllAssets returns all assets found in world state
|
|
func (s *BridgeChaincode) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
|
|
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resultsIterator.Close()
|
|
|
|
var assets []*Asset
|
|
for resultsIterator.HasNext() {
|
|
queryResponse, err := resultsIterator.Next()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var asset Asset
|
|
err = json.Unmarshal(queryResponse.Value, &asset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
assets = append(assets, &asset)
|
|
}
|
|
|
|
return assets, nil
|
|
}
|
|
|
|
func main() {
|
|
chaincode, err := contractapi.NewChaincode(&BridgeChaincode{})
|
|
if err != nil {
|
|
fmt.Printf("Error creating bridge chaincode: %v", err)
|
|
return
|
|
}
|
|
|
|
if err := chaincode.Start(); err != nil {
|
|
fmt.Printf("Error starting bridge chaincode: %v", err)
|
|
}
|
|
}
|