Files
2026-03-02 12:14:09 -08:00

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)
}
}